矛盾的协议一致性
Contradictory protocol conformances
谁能解释一下,这是怎么回事:
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
它会按预期工作。
符合 Test1
和 Test2
。起初我认为顺序很重要。但看起来只是互相抵消了!没有任何警告。这是非常违反直觉的。
注意: Test
符合 Equatable
而不是 因为你提供的两个扩展,但是因为auto-generated Equatable
实现。正如您所说,如果只有这两个扩展名,那么 ==
是 Equatable
实现将是模棱两可的。
Test1
或 Test2
协议扩展中的 ==
均未被调用。相反,调用 auto-generated Equatable
实现。您可能还记得,==
运算符对于属性全部为 Equatable
的类型来说是 auto-generated,并且被声明为符合 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
中,a
和b
都是Test
,所以选择了none个扩展运算符。但是,由于 Test1
和 Test2
都使用 Self
,您只能将它们用作通用约束,而不能转换为它们。因此,我认为您根本无法调用扩展中声明的 ==
。
无论如何,如果您想看到一条错误消息,指出有重复的 ==
运算符可用:
struct Test {
var value: Int
var x: Any? // now there is no auto-generated Equatable implementation!
}
error: type 'Test' does not conform to protocol 'Equatable' extension
Test: Test1 {} ^
note: candidate exactly matches
static func == (lhs: Self, rhs: Self) -> Bool {
^
note: candidate exactly matches
static func == (lhs: Self, rhs: Self) -> Bool {
^
如果您删除其中一个扩展名,那么 Test
由于扩展名 符合 Equatable
,因为不再有歧义。因此,auto-generated 实现不再是 auto-generated,只有一个 ==
可供选择 - 扩展中声明的那个。
谁能解释一下,这是怎么回事:
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
它会按预期工作。
符合 Test1
和 Test2
。起初我认为顺序很重要。但看起来只是互相抵消了!没有任何警告。这是非常违反直觉的。
注意: Test
符合 Equatable
而不是 因为你提供的两个扩展,但是因为auto-generated Equatable
实现。正如您所说,如果只有这两个扩展名,那么 ==
是 Equatable
实现将是模棱两可的。
Test1
或 Test2
协议扩展中的 ==
均未被调用。相反,调用 auto-generated Equatable
实现。您可能还记得,==
运算符对于属性全部为 Equatable
的类型来说是 auto-generated,并且被声明为符合 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
中,a
和b
都是Test
,所以选择了none个扩展运算符。但是,由于 Test1
和 Test2
都使用 Self
,您只能将它们用作通用约束,而不能转换为它们。因此,我认为您根本无法调用扩展中声明的 ==
。
无论如何,如果您想看到一条错误消息,指出有重复的 ==
运算符可用:
struct Test {
var value: Int
var x: Any? // now there is no auto-generated Equatable implementation!
}
error: type 'Test' does not conform to protocol 'Equatable' extension Test: Test1 {} ^
note: candidate exactly matches
static func == (lhs: Self, rhs: Self) -> Bool { ^
note: candidate exactly matches
static func == (lhs: Self, rhs: Self) -> Bool { ^
如果您删除其中一个扩展名,那么 Test
由于扩展名 符合 Equatable
,因为不再有歧义。因此,auto-generated 实现不再是 auto-generated,只有一个 ==
可供选择 - 扩展中声明的那个。