【发布时间】:2020-05-12 22:04:06
【问题描述】:
我在协议扩展和泛型的接口上看到了一些奇怪的行为。我是 Swift 新手,所以我可能会误解,但我不明白这怎么可能是正确的行为。
首先让我们定义一个协议,并使用默认函数实现对其进行扩展:
protocol Foo {
}
extension Foo {
static func yo() {
print("Foo.yo")
}
}
现在定义几个符合要求的类型:
struct A: Foo {
}
struct B: Foo {
static func yo() {
print("B.yo")
}
}
A.yo()
B.yo()
正如所料,A.yo() 使用yo 的默认实现,而B.yo() 使用B 提供的显式实现:输出为
Foo.yo
B.yo
现在让我们创建一个简单的泛型类型:
struct C<T: Foo> {
static func what() {
T.yo()
}
}
C<A>.what()
C<B>.what()
C<A>.what() 打印 Foo.yo,正如预期的那样。但是C<B>.what() 也会打印出Foo.yo!
C<B> 的含义肯定只是C 的模板,用B 代替了类型参数T?然而B 的yo 版本没有被调用。
我错过了什么?我正在使用 Swift 5.2.2。
现在,事实证明,您可以通过在 Foo 的原始定义中声明 yo 来解决此问题。如果我们这样做:
protocol Foo {
static func yo()
}
然后C<B>.what() 按我的预期工作,打印B.yo。一开始我无法理解最初的行为,但我更无法理解这将如何改变它。
在我的实际应用程序中,我无法使用此修复程序,因为我正在扩展一个预先存在的协议,其中包含一个我想专注于特定符合类型的函数。
【问题讨论】:
-
泛型在编译时解析。它们不像类层次结构或协议上的方法调用那样动态调度。静态是他们的重点,这就是性能获胜的根源。你应该让
yo成为协议的要求,然后它会被动态调度。 -
是的,我明白了。但是肯定 C 应该在编译时解决以它的什么方法调用 B.yo ?
-
我的评论太长了,我写了一个完整的答案。希望这会有所帮助
-
这可能是泛型无关紧要,这是stackoverflow.com/questions/31431753/… 的情况,但我不会担心。
-
@matt 是的,看起来确实很相似,除了在这种情况下,它显然是静态与动态调度的问题:我仍然想念它是如何产生的。在我看来,这是
struct C<T: Foo>的语义问题,其中T在泛型主体中被解释为Foo,而不是简单地将这个泛型限制为满足T的Foo,如我会预料到的。
标签: swift