Swift:带有协议和子类的动态调度

问题描述

请考虑以下 Swift 5 代码

protocol P: class {
    func call_foo()
    func foo()
    func call_bar()
    func bar()
}

extension P {
    func call_foo() { foo() }
    func foo() { print("P.foo") }
    func call_bar() { bar() }
    func bar() { print("P.bar") }
}

class C1: P {
    func foo() { print("C1.foo") }
}

class C2: C1 {
    func bar() { print("C2.bar") }
}

let c = C2()
c.call_foo()    //  C1.foo
c.foo()         //  C1.foo
c.call_bar()    //  P.bar
c.bar()         //  C2.bar

如果 foo() 中的 P.call_foo() 调用被动态分派到 C1.foo(),那么为什么 bar() 中的 P.call_bar() 调用没有被动态分派到 {{ 1}}?

唯一的区别是C2.bar()在符合foo()的类中被直接覆盖,而P只在子类中被覆盖。为什么会有所不同?

鉴于 bar() 是一项协议要求,对它的所有调用不应该总是动态调度吗?

解决方法

在您的扩展程序的上下文中:

extension P {
    func call_foo() { foo() }
    func foo() { print("P.foo") }
    func call_bar() { bar() }
    func bar() { print("P.bar") }
}

C2 不存在,P 是一个协议,方法是静态派发的,虽然bar()P 的一个要求,但它不是由{ {1}} 符合 C1 所以:

P

这是正常的,有趣的是你有:

let c1: some P = C1()
c1.call_foo()    //  C1.foo
c1.foo()         //  C1.foo
c1.call_bar()    //  P.bar
c1.bar()         //  P.bar

这意味着如果您只有对 let someP: some P = C2() someP.call_foo() // C1.foo someP.foo() // C1.foo someP.call_bar() // P.bar someP.bar() // P.bar 的引用,则 some P 的子类 C2 的行为与它的超类完全一样:C1 调用 call_bar() 因为 { {1}} 未实现 P.bar()

现在让我们看看如果您在 C1 中实现 bar() 会发生什么:

bar()

如果我们使用 C1 引用 class C1: P { func foo() { print("C1.foo") } func bar() { print("C1.bar") } } class C2: C1 { override func bar() { print("C2.bar") } }

C1

现在在 some P 中,编译器知道它必须使用 let c1: some P = C1() c1.call_foo() // C1.foo c1.foo() // C1.foo c1.call_bar() // C1.bar c1.bar() // C1.bar ,因此使用 call_bar() 引用 C1.bar()

C2

子类 some P 的行为方式仍然与它的超类 let someP: some P = C2() someP.call_foo() // C1.foo someP.foo() // C1.foo someP.call_bar() // C2.bar someP.bar() // C2.bar 相同,并且它调用了 C2 get 的实现。 (当 sublasses 表现得像它们的父级时,我发现它有点reassuring)。

现在让我们检查原始片段:

C1

它起作用了!