简体   繁体   English

使 swift 协议符合 Hashable

[英]Make a swift protocol conform to Hashable

I'm going around in circles trying to get Hashable to work with multiple struct that conform to the same protocol .我正在Hashable试图让Hashable与符合相同protocol多个struct一起工作。

I have a protocol SomeLocation declared like this:我有一个协议SomeLocation声明如下:

protocol SomeLocation {
    var name:String { get }
    var coordinates:Coordinate { get }
}

Then I create multiple objects that contain similar data like this:然后我创建多个包含类似数据的对象,如下所示:

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 {
        ...
    }
}

I can later use these in the same array by declaring:我稍后可以通过声明在同一个数组中使用这些:

let locations: [SomeLocation]

The problem is, I create an MKAnnotation subclass and need to use a custom Hashable on the SomeLocation objects.问题是,我创建了一个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
}

This gives me 2 errors:这给了我两个错误:

Value of type 'SomeLocation' has no member 'hashValue' Binary operator “SomeLocation”类型的值没有成员“hashValue”二元运算符

'==' cannot be applied to two 'SomeLocation' operands “==”不能应用于两个“SomeLocation”操作数

So I add the Hashable protocol to my SomeLocation protocol:所以我将Hashable协议添加到我的SomeLocation协议中:

protocol SomeLocation: Hashable {
    ...
}

This removes the first error of hashValue not being available, but now I get an error where I declared let location:SomeLocation saying这消除了 hashValue 不可用的第一个错误,但现在我收到一个错误,我声明了let location:SomeLocation

Protocol 'SomeLocation' can only be used as a generic constraint because it has Self or associated type requirements协议 'SomeLocation' 只能用作通用约束,因为它具有 Self 或关联类型要求

So it doesn't look like I can add Hashable to the protocol.所以看起来我不能将Hashable添加到协议中。

I can add Hashable directly to each struct that implements the SomeLocation protocol, however that means I need to use code like this and keep updating it every time I might make another object that conforms to the SomeLocation protocol.我可以将Hashable直接添加到实现SomeLocation协议的每个结构中,但这意味着我需要使用这样的代码并在每次我可能创建另一个符合SomeLocation协议的对象时不断更新它。

override var hash: Int {
    if let location = location as? ShopLocation
    {
        return location.hashValue
    }
    return self.hashValue
}

I have tried another way, by making a SomeLocationRepresentable struct:我尝试了另一种方法,通过创建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
    }
}

however when I try to use this in the LocationAnnotation class like但是当我尝试在LocationAnnotation类中使用它时

let location: SomeLocationRepresentable
init(location:SomeLocation) {
    self.location = SomeLocationRepresentable(with: location)
    super.init()
}

I get an error我收到一个错误

Value of protocol type 'SomeLocation' cannot conform to 'SomeLocation';协议类型“SomeLocation”的值不能符合“SomeLocation”; only struct/enum/class types can conform to protocols只有结构/枚举/类类型可以符合协议

Is it possible to achieve what I am trying to do?是否有可能实现我想要做的事情? Use objects that all conform to a protocol and use a custom Hashable to compare one to the other?使用所有符合协议的对象并使用自定义Hashable来比较一个对象?

Deriving the protocol from Hashable and using a type eraser might help here: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
    }
}

You then can simply declare the protocol conformance on the structs, and if Coordinate is already Hashable , then you don't need to write any extra hashing code code, since the compiler can automatically synthesize for you (and so will do for new types as long as all their properties are Hashable :然后您可以简单地在结构上声明协议一致性,如果Coordinate已经是Hashable ,那么您不需要编写任何额外的散列代码代码,因为编译器可以自动为您合成(对于新类型也是如此)只要它们的所有属性都是Hashable

struct ShopLocation: SomeLocation, Decodable {
    var name: String
    var coordinates: Coordinate
}

struct CarLocation: SomeLocation, Decodable {
    var name: String
    var coordinates: Coordinate
}

If Coordinate is also Codable , then you also can omit writing any code for the encoding/decoding operations, the compile will synthesize the required methods (provided all other properties are already Codable ).如果Coordinate也是Codable ,那么您也可以省略为编码/解码操作编写任何代码,编译将合成所需的方法(前提是所有其他属性都已经是Codable )。

You can then use the eraser within the annotation class by forwardingn the initializer constraints:然后,您可以通过转发初始化器约束在注释类中使用橡皮擦:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM