首页 > 解决方案 > 矛盾的协议一致性

问题描述

任何人都可以解释一下,这里发生了什么:

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。起初我认为顺序很重要。但看起来它只是相互抵消!没有任何警告。这是非常违反直觉的。

标签: swiftprotocolsswift5

解决方案


注意: Test符合Equatable 不是因为您提供的两个扩展,而是因为自动生成的Equatable实现。正如您所说,如果只有这两个扩展,那么实现将是模棱两可==Equatable

或协议扩展中==的任何一个都没有被调用。相反,会调用自动生成的实现。您可能还记得,运算符是为属性为 all 的类型自动生成的,并且被声明为符合自身。Test1Test2Equatable==EquatableEquatable

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

protocol P { }
class C : P { func f() { print("C") } }
extension P { func f() { print("P") } }
C().f() // C
(C() as P).f() // P

a == b, bothabare 中Test,因此没有选择任何扩展运算符。但是,由于Test1Test2都使用Self,因此您只能将它们用作通用约束,也不能强制转换为它们。因此,我认为您根本无法调用==扩展中的声明。

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

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

错误:类型'Test'不符合协议'Equatable'扩展测试:Test1 {} ^

注意:候选人完全匹配

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

注意:候选人完全匹配

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

如果您删除其中一个扩展名,则由于扩展名而Test符合,Equatable 因为不再有歧义。因此,自动生成的实现不再是自动生成的,只有一个==可供选择——扩展中声明的那个。


推荐阅读