[英]Swift 5.7 - Make existential 'any' protocol conform to Hashable
[英]Make a swift protocol conform to Hashable
我正在Hashable
試圖讓Hashable
與符合相同protocol
多個struct
一起工作。
我有一個協議SomeLocation
聲明如下:
protocol SomeLocation {
var name:String { get }
var coordinates:Coordinate { get }
}
然后我創建多個包含類似數據的對象,如下所示:
struct ShopLocation: SomeLocation, Decodable {
var name: String
var coordinates: Coordinate
init(from decoder: Decoder) throws {
...
}
}
struct CarLocation: SomeLocation, Decodable {
var name: String
var coordinates: Coordinate
init(from decoder: Decoder) throws {
...
}
}
我稍后可以通過聲明在同一個數組中使用這些:
let locations: [SomeLocation]
問題是,我創建了一個MKAnnotation
子類,並且需要在SomeLocation
對象上使用自定義Hashable
。
final class LocationAnnotation:NSObject, MKAnnotation {
let location:SomeLocation
init(location:SomeLocation) {
self.location = location
super.init()
}
}
override var hash: Int {
return location.hashValue
}
override func isEqual(_ object: Any?) -> Bool {
if let annot = object as? LocationAnnotation
{
let isEqual = (annot.location == location)
return isEqual
}
return false
}
這給了我兩個錯誤:
“SomeLocation”類型的值沒有成員“hashValue”二元運算符
“==”不能應用於兩個“SomeLocation”操作數
所以我將Hashable
協議添加到我的SomeLocation
協議中:
protocol SomeLocation: Hashable {
...
}
這消除了 hashValue 不可用的第一個錯誤,但現在我收到一個錯誤,我聲明了let location:SomeLocation
說
協議 'SomeLocation' 只能用作通用約束,因為它具有 Self 或關聯類型要求
所以看起來我不能將Hashable
添加到協議中。
我可以將Hashable
直接添加到實現SomeLocation
協議的每個結構中,但這意味着我需要使用這樣的代碼並在每次我可能創建另一個符合SomeLocation
協議的對象時不斷更新它。
override var hash: Int {
if let location = location as? ShopLocation
{
return location.hashValue
}
return self.hashValue
}
我嘗試了另一種方法,通過創建SomeLocationRepresentable
結構:
struct SomeLocationRepresentable {
private let wrapped: SomeLocation
init<T:SomeLocation>(with:T) {
wrapped = with
}
}
extension SomeLocationRepresentable: SomeLocation, Hashable {
var name: String {
wrapped.name
}
var coordinates: Coordinate {
wrapped.coordinates
}
func hash(into hasher: inout Hasher) {
hasher.combine(name)
hasher.combine(coordinates)
}
static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.name == rhs.name && lhs.coordinates == rhs.coordinates
}
}
但是當我嘗試在LocationAnnotation
類中使用它時
let location: SomeLocationRepresentable
init(location:SomeLocation) {
self.location = SomeLocationRepresentable(with: location)
super.init()
}
我收到一個錯誤
協議類型“SomeLocation”的值不能符合“SomeLocation”; 只有結構/枚舉/類類型可以符合協議
是否有可能實現我想要做的事情? 使用所有符合協議的對象並使用自定義Hashable
來比較一個對象?
從Hashable
派生協議並使用類型橡皮擦可能會有所幫助:
protocol SomeLocation: Hashable {
var name: String { get }
var coordinates: Coordinate { get }
}
struct AnyLocation: SomeLocation {
let name: String
let coordinates: Coordinate
init<L: SomeLocation>(_ location: L) {
name = location.name
coordinates = location.coordinates
}
}
然后您可以簡單地在結構上聲明協議一致性,如果Coordinate
已經是Hashable
,那么您不需要編寫任何額外的散列代碼代碼,因為編譯器可以自動為您合成(對於新類型也是如此)只要它們的所有屬性都是Hashable
:
struct ShopLocation: SomeLocation, Decodable {
var name: String
var coordinates: Coordinate
}
struct CarLocation: SomeLocation, Decodable {
var name: String
var coordinates: Coordinate
}
如果Coordinate
也是Codable
,那么您也可以省略為編碼/解碼操作編寫任何代碼,編譯將合成所需的方法(前提是所有其他屬性都已經是Codable
)。
然后,您可以通過轉發初始化器約束在注釋類中使用橡皮擦:
final class LocationAnnotation: NSObject, MKAnnotation {
let location: AnyLocation
init<L: SomeLocation>(location: L) {
self.location = AnyLocation(location)
super.init()
}
override var hash: Int {
location.hashValue
}
override func isEqual(_ object: Any?) -> Bool {
(object as? LocationAnnotation)?.location == location
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.