繁体   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