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
}

编译器告诉我:

Type 'SomeComponentOfTypeA' does not conform to protocol 'Component'

基本上,它不知道因为我正在实施 ComponentA,所以我的步骤也应该是 StepA 类型。

作为解决方法,我发现如果我修改这部分:

protocol Component {
    var step: Step { get }
}

进入:

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

一切正常,a.stepStepA,完全没有问题...

问题:为什么会这样?我基本上隐藏了 step 属性 来自 Component 类型 Step 的事实,通过使用关联类型 SomeStep。 这有点奇怪,不是吗?我原以为这可以正常工作而不必隐藏任何东西,为什么会这样?它是如何工作的?

谢谢。任何帮助将不胜感激!

您真的需要所有协议吗?您可以使用关联值执行此操作:

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. 协议组件有一个 step 类型的变量 Step(协议)
  2. 变量 step 在协议 ComponentA
  3. 中被重新声明
  4. 因此,在定义 SomeComponentOfTypeA 时,编译器给出错误,因为它发现 step 的类型为 StepA per ComponentA 但不是类型 Step per Component协议。
  5. 定义 AssociatedType 时,您可以使用泛型来指定 Component 中的 step 变量可以是遵循协议 Step
  6. 的任何类型
  7. 所以现在ComponentA的step变量满足第4点,因此没有错误。

因此,如果您真的想在 ComponentA 中为特定类型 StepA.

协议可以被结构或枚举采用,所以它不做 class 多态性,因为它们不能被继承。这也是associatedtype存在的原因之一。

回到你的问题,你在子协议中定义了一个与父协议同名的变量,但类型不同,这是允许的,因为协议不是具体类型。但这不是类型重载,因为协议的工作方式不同于 class.

在这种情况下,你最好使用泛型,也就是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))

输出:

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'
}