【问题标题】:Contradictory protocol conformances矛盾的协议一致性
【发布时间】:2020-12-31 20:44:11
【问题描述】:

谁能解释一下,这是怎么回事:

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

【问题讨论】:

    标签: swift protocols swift5


    【解决方案1】:

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

    Test1Test2 协议扩展中的 == 均未调用。而是调用自动生成的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 == b 中,ab 都是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因为有扩展名,因为不再有歧义。因此,自动生成的实现不再自动生成,只有一个== 可供选择——扩展中声明的那个。

    【讨论】:

    • "因此,我认为您根本无法调用扩展中声明的 ==。"正如我在帖子中提到的,如果只选择一个扩展,则 == 该扩展被调用。
    • @Roman 当然可以。我的意思是“如果你保留两个扩展名,你就不能调用它们中的任何一个”。我在回复 Roman Ryzhiy 的评论中解释了原因。
    • @Roman 我已将该评论的内容移至答案中。请检查。有意义吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-20
    • 1970-01-01
    相关资源
    最近更新 更多