[英]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.