因为Equatable 有Self 要求,所以不应该直接在协议上实现。否则,协议将无法用作类型。
要在协议级别实现Equatable 并能够将协议用作类型,您可以使用类型擦除。
为了演示,我修改了你的 Playground 中给出的代码来构建一个类型橡皮擦。
有关我使用的方法的详细说明,请查看我博客上的这篇文章:
https://khawerkhaliq.com/blog/swift-protocols-equatable-part-two/
这是您的 Playground 中修改后的代码:
protocol CustomProtocol {
var title: String { get }
var subtitle: String { get }
func isEqualTo(_ other: CustomProtocol) -> Bool
func asEquatable() -> AnyEquatableCustomProtocol
}
extension CustomProtocol where Self: Equatable {
func isEqualTo(_ other: CustomProtocol) -> Bool {
guard let o = other as? Self else { return false }
return self == o
}
func asEquatable() -> AnyEquatableCustomProtocol {
return AnyEquatableCustomProtocol(self)
}
}
struct A: CustomProtocol, Equatable {
var title: String
var subtitle: String
static func ==(lhs: A, rhs: A) -> Bool {
return lhs.title == rhs.title && lhs.subtitle == rhs.subtitle
}
}
struct B: CustomProtocol, Equatable {
var title: String
var subtitle: String
static func ==(lhs: B, rhs: B) -> Bool {
return lhs.title == rhs.title && lhs.subtitle == rhs.subtitle
}
}
struct AnyEquatableCustomProtocol: CustomProtocol, Equatable {
var title: String { return value.title }
var subtitle: String { return value.subtitle }
init(_ value: CustomProtocol) { self.value = value }
private let value: CustomProtocol
static func ==(lhs: AnyEquatableCustomProtocol, rhs: AnyEquatableCustomProtocol) -> Bool {
return lhs.value.isEqualTo(rhs.value)
}
}
// instances typed as the protocol
let a: CustomProtocol = A(title: "First title", subtitle: "First subtitle")
let b: CustomProtocol = B(title: "First title", subtitle: "First subtitle")
let equalA: CustomProtocol = A(title: "First title", subtitle: "First subtitle")
let unequalA: CustomProtocol = A(title: "Second title", subtitle: "Second subtitle")
// equality tests
print(a.asEquatable() == b.asEquatable()) // prints false
print(a.asEquatable() == equalA.asEquatable()) // prints true
print(a.asEquatable() == unequalA.asEquatable()) // prints false
需要注意的是,通过这种方法,实际的== 比较被委托给底层的具体类型,但我们只处理协议类型以保持抽象。
这里我只使用类型擦除实例进行比较。但是,由于类型擦除器符合CustomProtocol,这些实例可以保存并在任何需要协议类型的地方使用。因为它们符合Equatable,所以它们也可以用于任何需要Equatable 一致性的地方。
就上下文而言,这篇文章解释了为什么不建议尝试直接在协议上实现Equatable 一致性甚至== 函数:
https://khawerkhaliq.com/blog/swift-protocols-equatable-part-one/
因此类型擦除。
希望这会有所帮助。