避免 Equatable 和 Hashable 样板,Swift 4.2
Avoid Equatable and Hashable boilerplate, Swift 4.2
在项目中,我们使用 类 作为模型层,因此我必须编写如下代码:
// MARK: - Hashable
extension Player: Hashable {
static func == (lhs: Player, rhs: Player) -> Bool {
return lhs.hashValue == rhs.hashValue
}
func hash(into hasher: inout Hasher) {
hasher.combine(self.name)
}
}
能否以某种方式避免这种样板文件?是否可以默认实现 Equatable
与 .hashValue
比较?谢谢
这是错误的,编译器自动合成是没有意义的:
static func == (lhs: Player, rhs: Player) -> Bool {
return lhs.hashValue == rhs.hashValue
}
相同的对象必须具有相同的哈希值,但反之则不然:不同的对象可以具有相同的哈希值。
具体来说,在你的例子中:名称是一个字符串,有无限多个不同的字符串,但只有 264 个不同的哈希值。所以一定有两个不同的字符串具有相同的哈希值。
如果所有存储的属性都是Hashable
,那么编译器可以为您完全合成一致性。例如
struct Player : Equatable, Hashable {
let name: String
var score: Int
}
这里如果两个玩家有相同的名字和相同的分数,则他们是“相同的”。
如果有non-hashable属性,或者你想自定义身份的概念那么你就得重写==
和hash(into)
因此。散列函数应使用与确定 ==
中的身份相同的属性。例如
struct Player : Equatable, Hashable {
let name: String
var score: Int
static func == (lhs: Player, rhs: Player) -> Bool {
return lhs.name == rhs.name
}
func hash(into hasher: inout Hasher) {
hasher.combine(self.name)
}
}
现在,如果两个玩家的名字相同,则他们是“相同的”。
您可以通过 Stencil 标记语言编写自定义模板,并使用 Sourcery 库自动生成代码。
或使用现有解决方案(AutoEquatable、AutoHashable Sourcery 模板)。
你也可以这样写:
protocol IHash: class { }
extension IHash where Self: Hashable {
static func ==(lhs: Self, rhs: Self) -> Bool {
return lhs.hashValue == rhs.hashValue
}
}
class User: IHash, Hashable {
var name: String = ""
func hash(into hasher: inout Hasher) {
hasher.combine(self.name)
}
}
它将帮助您避免在不同 类.
中重复
下面的使用是错误的,原因是哈希冲突
static func == (lhs: Player, rhs: Player) -> Bool {
return lhs.hashValue == rhs.hashValue
}
散列是作为一种 'fast' 比较对象的方法而发明的。
如果 2 个对象具有不同的哈希值,则 保证 它们是不同的。
但如果它们具有相同的哈希值,我们不确定它们是否相同。
因此需要 static func == (lhs: Player, rhs: Player) -> Bool
作为后备方法,以便在哈希值相同时使用,以确保对象确实相同。
因此,如果您使用 .hashvalue
作为实现,那么有时您会因为散列冲突而得到错误的结果。
为了加速 Hashable 协议的实施,我建议您开始在您的项目中使用 sourcery。
在项目中,我们使用 类 作为模型层,因此我必须编写如下代码:
// MARK: - Hashable
extension Player: Hashable {
static func == (lhs: Player, rhs: Player) -> Bool {
return lhs.hashValue == rhs.hashValue
}
func hash(into hasher: inout Hasher) {
hasher.combine(self.name)
}
}
能否以某种方式避免这种样板文件?是否可以默认实现 Equatable
与 .hashValue
比较?谢谢
这是错误的,编译器自动合成是没有意义的:
static func == (lhs: Player, rhs: Player) -> Bool {
return lhs.hashValue == rhs.hashValue
}
相同的对象必须具有相同的哈希值,但反之则不然:不同的对象可以具有相同的哈希值。
具体来说,在你的例子中:名称是一个字符串,有无限多个不同的字符串,但只有 264 个不同的哈希值。所以一定有两个不同的字符串具有相同的哈希值。
如果所有存储的属性都是Hashable
,那么编译器可以为您完全合成一致性。例如
struct Player : Equatable, Hashable {
let name: String
var score: Int
}
这里如果两个玩家有相同的名字和相同的分数,则他们是“相同的”。
如果有non-hashable属性,或者你想自定义身份的概念那么你就得重写==
和hash(into)
因此。散列函数应使用与确定 ==
中的身份相同的属性。例如
struct Player : Equatable, Hashable {
let name: String
var score: Int
static func == (lhs: Player, rhs: Player) -> Bool {
return lhs.name == rhs.name
}
func hash(into hasher: inout Hasher) {
hasher.combine(self.name)
}
}
现在,如果两个玩家的名字相同,则他们是“相同的”。
您可以通过 Stencil 标记语言编写自定义模板,并使用 Sourcery 库自动生成代码。
或使用现有解决方案(AutoEquatable、AutoHashable Sourcery 模板)。
你也可以这样写:
protocol IHash: class { }
extension IHash where Self: Hashable {
static func ==(lhs: Self, rhs: Self) -> Bool {
return lhs.hashValue == rhs.hashValue
}
}
class User: IHash, Hashable {
var name: String = ""
func hash(into hasher: inout Hasher) {
hasher.combine(self.name)
}
}
它将帮助您避免在不同 类.
中重复下面的使用是错误的,原因是哈希冲突
static func == (lhs: Player, rhs: Player) -> Bool {
return lhs.hashValue == rhs.hashValue
}
散列是作为一种 'fast' 比较对象的方法而发明的。 如果 2 个对象具有不同的哈希值,则 保证 它们是不同的。
但如果它们具有相同的哈希值,我们不确定它们是否相同。
因此需要 static func == (lhs: Player, rhs: Player) -> Bool
作为后备方法,以便在哈希值相同时使用,以确保对象确实相同。
因此,如果您使用 .hashvalue
作为实现,那么有时您会因为散列冲突而得到错误的结果。
为了加速 Hashable 协议的实施,我建议您开始在您的项目中使用 sourcery。