簡體   English   中英

Swift 中的協議向下轉換

[英]Downcasting on protocols in Swift

我有以下代碼:

protocol Step { /* body */ }

enum StepA: Int, CaseIterable, Step {
    case one
    case two
    case three
}

enum StepB: Int, CaseIterable, Step {
    case one
    case two
    case three
}

protocol Component {
    var step: Step { get }
}

protocol ComponentA: Component {
    var step: StepA { get }
}

protocol ComponentB: Component {
    var step: StepB { get }
}


struct SomeComponentOfTypeA: ComponentA {
    var step: StepA = StepA.one
}


let a = SomeComponentOfTypeA()

print(a.step)

a.step的類型我想成為StepA ,因為我正在以這種方式初始化。 但是,我不能這樣做,因為:

struct SomeComponentOfTypeA: ComponentA {
    var step: StepA = StepA.one
}

編譯器告訴我:

類型“SomeComponentOfTypeA”不符合協議“組件”

基本上,它不知道因為我正在實現ComponentA ,所以我的步驟也應該是StepA類型。

作為一種解決方法,我發現如果我修改這部分:

protocol Component {
    var step: Step { get }
}

進入:

protocol Component {
    associatedtype SomeStep: Step
    var step: SomeStep { get }
}

一切正常, a.step屬於StepA ,完全沒有問題......

問題:為什么這有效? 我基本上是通過使用關聯類型SomeStep來隱藏Componentstep屬性是Step類型的事實。 這有點奇怪,不是嗎? 我希望這可以正常工作而不必隱藏任何東西,為什么會發生這種情況? 它是如何工作的?

謝謝。 任何幫助將非常感激!

你真的需要所有的協議嗎? 您可以使用關聯值執行此操作:

protocol Step { /* body */ }

enum StepA: Int, CaseIterable, Step {
    case one
    case two
    case three
}

enum StepB: Int, CaseIterable, Step {
    case one
    case two
    case three
}

enum Component {
    case typeA(StepA)
    case typeB(StepB)
}

let component = Component.typeA(StepA.one)

錯誤在於協議的定義方式。

  1. Protocol Component 有一個Step類型的變量step (一個協議)
  2. 變量step在協議 ComponentA 中重新聲明
  3. 因此,在定義SomeComponentOfTypeA時,編譯器會給出一個錯誤,因為它發現每個ComponentAStepA類型的step但不是每個Component協議的Step類型的 step。
  4. 定義 AssociatedType 時,然后使用 generics 來指定Component中的step變量可以是遵循協議Step的任何類型
  5. 所以現在 ComponentA 的 step 變量滿足第 4 點,因此沒有錯誤。

因此,如果您真的想在ComponentA中為特定類型StepA重新聲明變量step ,那么您最終使用 generics 所做的就是這種情況的正確方法。

結構或枚舉可以采用協議,因此它不會執行 class 多態性,因為它們不能被繼承。 這也是為什么會有associatedtype存在的原因之一。

回到您的問題,您在子協議中定義了一個與父協議同名的變量,但類型不同,這是允許的,因為協議不是具體類型。 但這不是類型重載,因為該協議的工作方式與 class 不同。

在這種情況下,您最好使用 generics,即associatedtype類型。 但是如果非要使用這三個協議,只需要聲明子協議即可。

...

protocol ComponentA: Component {}

protocol ComponentB: Component {}

struct SomeComponentOfTypeA: ComponentA {
  var step: Step = StepA.one
}

struct SomeComponentOfTypeB: ComponentB {
  var step: Step = StepB.two
}

let a = SomeComponentOfTypeA()

print(a.step)
print(type(of: a))

let b = SomeComponentOfTypeB()

print(b.step)
print(type(of: b))

Output:

one
SomeComponentOfTypeA
two
SomeComponentOfTypeB

另一種方法是滿足您的需求。 限制SomeComponentOfTypeA只允許StepA類型。

protocol StepAProtocol { /* body */ }
protocol StepBProtocol { /* body */ }

enum StepA: Int, CaseIterable, StepAProtocol {
    case one
    case two
    case three
}

enum StepB: Int, CaseIterable, StepBProtocol {
    case one
    case two
    case three
}

protocol Component {
  associatedtype T
  var step: T { get }
}

protocol ComponentA: Component where T: StepAProtocol  {}
protocol ComponentB: Component where T: StepBProtocol  {}

struct SomeComponentOfTypeA: ComponentA {
  var step: StepA = .one
}

struct SomeComponentOfTypeB: ComponentB {
  var step: StepB = .two
}

struct SomeComponentOfTypeC: ComponentB {
  var step: StepA = .two // error: Type 'StepA' does not conform to protocol 'StepBProtocol'
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM