目前我有一个泛型类型的类,我想通过搜索这个类的对象
contains()
方法,通过使类符合Hashable协议并为每个对象提供哈希值。现在我的问题是,我有完全相同属性的对象,并且数组似乎无法真正区分它们(我目前的方法是使用其中一个属性的哈希值作为类的哈希值,并且
== <T> (lhs: ClassA<T>, rhs: ClassA<T>) -> Bool
函数是通过比较散列值来完成的)。我尝试过使用像"id"这样的静态属性,但对于泛型类型,不支持静态属性。
我应该如何定义哈希值,以便仍然可以区分具有相同属性的不同对象?
EDIT:我直接使它符合Hashable,因为它也在程序的其他部分用作dict中的键,因为Hashable已经符合Equatable。
我目前的方法是使用其中一个属性的哈希值作为类的哈希值和
== <T> (lhs: ClassA<T>, rhs: ClassA<T>) -> Bool
函数是通过比较散列值来完成的
==
和hashValue
关系不是这样工作的——不要这样做。如果发生哈希冲突怎么办?具有不同属性的两个不同实例可以进行比较。
相反,您应该实现==
以实际比较两个实例的属性。如果两个给定实例具有等效属性,则==
应返回true
。如果两个实例的hashValue
与==
比较相等,则它们应该相等。
现在,除非T
是Equatable
,否则很可能无法进行此比较。对此的一个解决方案是不使ClassA
符合Equatable
,而是在T
为Equatable
时使==
过载,例如:
func == <T : Equatable>(lhs: ClassA<T>, rhs: ClassA<T>) -> Bool {
// stub: do comparison logic
}
现在,您只需将Sequence
的contains(where:)
方法与==
重载结合使用,即可检查给定实例是否在数组中:
var array = [ClassA("foo")] // assuming ClassA has an init(_: T) and a suitable ==
// implementation to compare that value
let someInstanceToFind = ClassA("foo")
print(array.contains { $0 == someInstanceToFind }) // true
如果您希望ClassA
具有hashValue
,那么只需在T
为Hashable
:时编写一个定义hashValue
的extension
extension ClassA where T : Hashable {
var hashValue: Int {
return 0 // to do: implement hashValue logic
}
}
不幸的是,这确实意味着ClassA
不会明确地符合Hashable
,而T
符合——但它将有一个hashValue
和==
实现。SE-0143:如果满足给定的where
条款,则条件一致性将通过允许对协议的明确一致性来改变这一点,但这一点尚未实现。
如果您需要显式地符合Hashable
(例如在Set
或Dictionary
密钥中使用类的实例),那么一个解决方案是创建一个包装器类型:
struct HashableClassA<T : Hashable> : Hashable {
var base: ClassA<T>
init(_ base: ClassA<T>) {
self.base = base
}
static func ==(lhs: HashableClassA, rhs: HashableClassA) -> Bool {
return lhs.base == rhs.base
}
var hashValue: Int {
return base.hashValue
}
}
现在,在添加到Set
或Dictionary
之前,您只需要将ClassA<T>
实例包装在HashableClassA
实例中。
刚刚意识到有一种简单的方法可以在中实现Equatable
contains()
方法:使用
return lhs === rhs
在==函数中,以便直接比较对象。它现在正以这种方式工作。