Давайте начнем с вашего CustomEquatable
протокола, без расширения:
protocol CustomEquatable {
func isEqualTo(_ other: CustomEquatable) -> Bool
}
Давайте определим некоторые типы для использования в экспериментах:
struct A: Equatable {
let name: String
}
struct B: Equatable {
let id: Int
}
Предположим, мы хотим, чтобы A
и B
соответствовали CustomEquatable
.Затем мы рассмотрим четыре случая:
- Что означает
a1.isEqualTo(a2)
(где a1
и a2
имеют тип A
)? - Что означает
b1.isEqualTo(b2)
означает (где b1
и b2
имеют тип B
)? - Что означает
a.isEqualTo(b)
(где a
- A
, а b
- B
)? - Что означает
b.isEqualTo(a)
(где b
- B
, а a
- A
)?
Для первых двухварианты, возможные ответы: a1.isEqualTo(a2)
тогда и только тогда, когда a1 == a2
и b1.isEqualTo(b2)
тогда и только тогда, когда b1 == b2
.
Для вторых двух случаев нам нужно решить, есть ли способ дляA
равно B
.Самое простое решение (я думаю) состоит в том, что A
никогда не может равняться B
.
Таким образом, мы можем записать соответствия следующим образом:
extension A: CustomEquatable {
func isEqualTo(_ other: CustomEquatable) -> Bool {
return (other as? A) == self
}
}
extension B: CustomEquatable {
func isEqualTo(_ other: CustomEquatable) -> Bool {
return (other as? B) == self
}
}
Единственная разница в этихдва соответствия - тип приведения (с правой стороны as?
).Таким образом, мы можем выделить соответствия в расширение протокола следующим образом:
extension CustomEquatable where Self: Equatable {
func isEqualTo(_ other: CustomEquatable) -> Bool {
return (other as? Self) == self
}
}
С этим расширением протокола мы можем заставить A
и B
соответствовать CustomEquatable
без реализации isEqualTo
для каждого:
extension A: CustomEquatable { }
extension B: CustomEquatable { }
Для проверки кода:
let a1 = A(name: "a1")
let a2 = A(name: "a2")
let b1 = B(id: 1)
let b2 = B(id: 2)
a1.isEqualTo(a1) // true
a1.isEqualTo(a2) // false
b1.isEqualTo(b1) // true
b1.isEqualTo(b2) // false
a1.isEqualTo(b1) // false
b1.isEqualTo(a1) // false