如何使用 Xcode 10 中提供的 API 使枚举符合 Hashable?

Rom*_*mov 4 hashable swift xcode10 swift4.2

在我的 Swift 4.2.1 代码中,我有这个枚举:

enum MyEnum {

    case caseOne(Int)
    case caseTwo(String)
    case caseThree
}
Run Code Online (Sandbox Code Playgroud)

它符合Equatable

extension MyEnum: Equatable {

    static func == (lhs: MyEnum, rhs: MyEnum) -> Bool {

        switch (lhs, rhs) {
        case (.caseOne, .caseOne), (.caseTwo, .caseTwo), (.caseThree, .caseThree):
            return true
        default:
            return false
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我需要让它符合Hashable,这就是为什么我添加了一个扩展:

extension MyEnum: Hashable {

    var hashValue: Int {

        switch self {
        case .caseOne:
            return 1
        case .caseTwo:
            return 2
        case .caseThree:
            return 3
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我想迁移到 Xcode 10 中可用的新 API。我删除了我的实现hashValue并添加了hash(into:)

extension MyEnum: Hashable {

    func hash(into hasher: inout Hasher) {

        switch self {
        case .caseOne:
            hasher.combine(1)
        case .caseTwo:
            hasher.combine(2)
        case .caseThree:
            hasher.combine(3)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

你能告诉我我是否正确切换到新的 API?我使用这个测试,true如果一切正常,它会打印两次:

var testDictionary = [MyEnum: Int]()
testDictionary[.caseOne(100)] = 100
testDictionary[.caseOne(1000)] = 1000
testDictionary[.caseTwo("100")] = 100
testDictionary[.caseTwo("1000")] = 1000
let countCaseOne = testDictionary.reduce(0) {
    if case .caseOne = $1.key {
        return $0 + 1
    }
    return $0
} == 1
print(countCaseOne) // true
let countCaseTwo = testDictionary.reduce(0) {
    if case .caseTwo = $1.key {
        return $0 + 1
    }
    return $0
} == 1
print(countCaseTwo) // true
Run Code Online (Sandbox Code Playgroud)

Dáv*_*tor 17

无需Hashable手动实现一致性,编译器可以根据您的具体情况自动综合一致性enum(其中具有关联值的所有情况都有Hashable关联值)。您只需声明一致性即可。

enum MyEnum: Hashable {
    case caseOne(Int)
    case caseTwo(String)
    case caseThree
}

// You don't even need to write `: Equatable`, since automatic Hashable conformance takes care of Equatable too, I just left it there for clarity
extension MyEnum: Equatable {
    static func == (lhs: MyEnum, rhs: MyEnum) -> Bool {
        switch (lhs, rhs) {
        case (.caseOne, .caseOne), (.caseTwo, .caseTwo), (.caseThree, .caseThree):
            return true
        default:
            return false
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • IFF 枚举的所有关联值也是“可哈希的”(或者没有)。 (3认同)

use*_*434 5

您可以使用自动生成的Hashable一致性,如另一个答案中所建议的(在您的类型不包含任何非Hashable类型日期的情况下)。

但这就是您在一般情况下可以做的事情(自动生成的代码也可能看起来像那样):

extension MyEnum: Hashable {

    func hash(into hasher: inout Hasher) {

        switch self {
        case .caseOne(let value):
            hasher.combine(value) // combine with associated value, if it's not `Hashable` map it to some `Hashable` type and then combine result
        case .caseTwo(let value):
            hasher.combine(value) // combine with associated value, if it's not `Hashable` map it to some `Hashable` type and then combine result
        case .caseThree:
            // you can `combine` with some `Hashable` constant, but here it's ok just to skip
            break
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 因为`hasher` 已经有一些初始状态(我们称之为`seed`)然后`combine(:)` 用`hasher` 状态(`seed` 初始)执行一些功能,然后组合值,结果替换状态“哈希器”。然后 `hasher` 的状态被转换为 `hash` 函数结果,它可以通过 `hashValue` 属性访问。因此,即使您根本不会“组合”,您仍然会得到结果。Ofc,如果你有多个没有“关联值”的“case”,你最好将它与一些“constant”结合起来,否则这些 case 的哈希值将是相同的。 (3认同)