使快速协议符合Hasable [英] Make a swift protocol conform to Hashable

查看:73
本文介绍了使快速协议符合Hasable的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在绕圈,试图让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"只能用作泛型约束,因为它具有自身或关联的类型要求

因此看起来我无法将Hashable添加到协议。

我可以将Hashable直接添加到实现SomeLocation协议的每个结构中,但是这意味着我需要使用这样的代码,并在每次可能创建另一个符合SomeLocation协议的对象时不断更新它。

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

我尝试了另一种方法,通过创建SomeLocationRepresentablestruct:

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";只有struct/enum/class类型才能符合协议

我想做的事情有可能实现吗?是否使用全部符合协议的对象并使用自定义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
    }
}

这篇关于使快速协议符合Hasable的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆