矛盾的协议一致性

时间:2020-09-14 06:21:02

标签: swift protocols swift5

任何人都可以解释一下,这是怎么回事:

struct Test {
    var value: Int
}

// -----------------------------------
protocol Test1: Equatable {
    var value: Int { get set }
}
extension Test1 {
    static func == (lhs: Self, rhs: Self) -> Bool {
        lhs.value == rhs.value + 1
    }
}
// -----------------------------------
protocol Test2: Equatable {
    var value: Int { get set }
}
extension Test2 {
    static func == (lhs: Self, rhs: Self) -> Bool {
        lhs.value == rhs.value + 2
    }
}
// -----------------------------------

extension Test: Test1 {}
extension Test: Test2 {}

let a = Test(value: 5)
let b = Test(value: 5)

print(a == b) // true, which is unexpected

如果仅符合Test1或仅符合Test2,则按预期工作。
符合Test1Test2。起初,我认为顺序很重要。但是看起来它只是互相抵消了!没有任何警告。这很违反直觉。

1 个答案:

答案 0 :(得分:3)

注意: Test符合Equatable 不是,因为您提供了两个扩展名,但由于自动生成的{{1 }}实现。如您所说,如果只有这两个扩展名,那么Equatable==的实现将是不确定的。

Equatable==协议扩展中的Test1都不被调用。而是调用自动生成的Test2实现。您可能还记得,Equatable运算符是为属性均为==的类型自动生成的,并声明为符合Equatable本身。

这是因为扩展中声明的成员使用静态绑定,因此,当同一成员有多个版本可用时,仅当编译时类型是扩展的类型时才选择扩展中声明的版本。例如:

Equatable

protocol P { } class C : P { func f() { print("C") } } extension P { func f() { print("P") } } C().f() // C (C() as P).f() // P 中,a == ba均为b,因此没有选择任何扩展运算符。但是,由于TestTest1都使用Test2,因此您只能将它们用作一般约束,也不能强制转换为约束。因此,我认为您根本无法调用扩展名中声明的Self

无论如何,如果您想看到一条错误消息,指出存在重复的==个运算符:

==

错误:“测试”类型与协议“ Equatable”扩展名不符 测试:Test1 {} ^

注意:候选人完全匹配

struct Test {
    var value: Int
    var x: Any? // now there is no auto-generated Equatable implementation!
}

注意:候选人完全匹配

   static func == (lhs: Self, rhs: Self) -> Bool {
               ^

如果您删除其中一个扩展名,则由于扩展名的存在, static func == (lhs: Self, rhs: Self) -> Bool { ^ 符合Test ,因为不再有歧义。因此,自动生成的实现不再自动生成,只有一个Equatable可供选择-扩展中声明的实现。