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.step是StepA,完全没有问题...
问题:为什么会这样?我基本上隐藏了 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)
错误在于协议的定义方式。
- 协议组件有一个
step
类型的变量 Step
(协议)
- 变量
step
在协议 ComponentA 中被重新声明
- 因此,在定义
SomeComponentOfTypeA
时,编译器给出错误,因为它发现 step
的类型为 StepA
per ComponentA
但不是类型 Step
per Component
协议。
- 定义 AssociatedType 时,您可以使用泛型来指定
Component
中的 step
变量可以是遵循协议 Step
的任何类型
- 所以现在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'
}
我有以下代码:
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.step是StepA,完全没有问题...
问题:为什么会这样?我基本上隐藏了 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)
错误在于协议的定义方式。
- 协议组件有一个
step
类型的变量Step
(协议) - 变量
step
在协议 ComponentA 中被重新声明
- 因此,在定义
SomeComponentOfTypeA
时,编译器给出错误,因为它发现step
的类型为StepA
perComponentA
但不是类型Step
perComponent
协议。 - 定义 AssociatedType 时,您可以使用泛型来指定
Component
中的step
变量可以是遵循协议Step
的任何类型
- 所以现在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'
}