避免 Equatable 和 Hashable 样板,Swift 4.2

Posted

技术标签:

【中文标题】避免 Equatable 和 Hashable 样板,Swift 4.2【英文标题】:Avoid Equatable and Hashable boilerplate, Swift 4.2 【发布时间】:2019-06-11 09:03:04 【问题描述】:

在项目中,我们使用类作为模型层,因此我必须编写如下代码:

// 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)
    

可以以某种方式避免这种样板吗?默认情况下是否可以通过.hashValue 实现Equatable 比较?谢谢。

【问题讨论】:

您可以将Sourcery 与自定义模板一起使用,并将您的类扩展为autoHashable 例如。 @EternalBlack 在 Swift 4.2 中使用 Sourcery 生成 Equatable 和 Hashable 是不必要的。顺便说一句,自动生成的代码并不比手动编写的代码好。样板仍然存在。生成的代码中甚至可能存在错误。 【参考方案1】:

这是错误的,编译器自动合成它是没有意义的:

static func == (lhs: Player, rhs: Player) -> Bool 
    return lhs.hashValue == rhs.hashValue

相同的对象必须具有相同的哈希值,但不能相反:不同的对象可以具有相同的哈希值。

具体而言,在您的示例中:名称是一个字符串,并且有无限多个不同的字符串,但只有 264 个不同的哈希值。所以必须有两个不同的字符串具有相同的哈希值。

如果所有存储的属性都是Hashable,那么编译器可以为您完全合成一致性。例如

struct Player : Equatable, Hashable 
    let name: String
    var score: Int

如果两个玩家的名字和分数相同,则他们是“相同的”。

如果有不可散列的属性,或者如果你想自定义身份的概念,那么你必须相应地覆盖== 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)
    

现在,如果两个玩家的名字相同,则他们是“相同的”。

【讨论】:

很抱歉,但我不明白为什么我的 Equtable 实现是错误的,您能否举例说明它可能导致意外行为的情况? @BohdanSavych:名字是一个字符串,有无数个不同的字符串,但只有2^64个不同的哈希值。所以必须有两个不同的字符串具有相同的哈希值。【参考方案2】:

您可以通过 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)
    

它将帮助您避免在不同类中的重复。

【讨论】:

谢谢安德鲁,这正是我所需要的 不,只比较 == 中的哈希值是错误的。

以上是关于避免 Equatable 和 Hashable 样板,Swift 4.2的主要内容,如果未能解决你的问题,请参考以下文章

Swift 中的“open”关键字是啥?

swift标准库中常见的55个协议。

尝试符合可等式泛型 Set 时,类型“任何”不符合协议“等式”

带有和不带有 Equatable 的 Flutter bloc 导航

Swift的Equatable和Comparable的使用

CLLocation 如何实现 Equatable 协议?