Dictionary 如何使用Swift枚举作为字典键?(符合同等标准)

Dictionary 如何使用Swift枚举作为字典键?(符合同等标准),dictionary,enums,swift,hashable,Dictionary,Enums,Swift,Hashable,我已经定义了一个枚举来表示一个“站”的选择;桩号由唯一的正整数定义,因此我创建了以下枚举,以允许负值表示特殊选择: enum StationSelector : Printable { case Nearest case LastShown case List case Specific(Int) func toInt() -> Int { switch self { case .Nearest:

我已经定义了一个枚举来表示一个“站”的选择;桩号由唯一的正整数定义,因此我创建了以下枚举,以允许负值表示特殊选择:

enum StationSelector : Printable {
    case Nearest
    case LastShown
    case List
    case Specific(Int)

    func toInt() -> Int {
        switch self {
        case .Nearest:
            return -1
        case .LastShown:
            return -2
        case .List:
            return -3
        case .Specific(let stationNum):
            return stationNum
        }
    }

    static func fromInt(value:Int) -> StationSelector? {
        if value > 0 {
            return StationSelector.Specific(value)
        }
        switch value {
        case -1:
            return StationSelector.Nearest
        case -2:
            return StationSelector.LastShown
        case -3:
            return StationSelector.List
        default:
            return nil
        }
    }

    var description: String {
    get {
        switch self {
        case .Nearest:
            return "Nearest Station"
        case .LastShown:
            return "Last Displayed Station"
        case .List:
            return "Station List"
        case .Specific(let stationNumber):
            return "Station #\(stationNumber)"
        }
    }
    }
}
我想将这些值用作字典中的键。声明字典会产生预期错误,即StationSelector不符合Hashable。通过一个简单的哈希函数,很容易符合Hashable:

var hashValue: Int {
get {
    return self.toInt()
}
}
但是,
Hashable
要求符合
equalable
,而且我似乎无法在枚举上定义equals运算符来满足编译器的要求

func == (lhs: StationSelector, rhs: StationSelector) -> Bool {
    return lhs.toInt() == rhs.toInt()
}
编译器抱怨这是一行中的两个声明,并希望放置一个
func
之后,这也没有意义

有什么想法吗?

有关将枚举作为字典键的信息: 从Swift手册:

没有关联值的枚举成员值(如中所述 默认情况下,枚举)也是可散列的

但是,您的枚举确实有一个带有关联值的成员值,因此您必须手动添加
Hashable
一致性

解决方案 您的实现存在的问题是,Swift中的运算符声明必须在全局范围内

只要动一下:

func == (lhs: StationSelector, rhs: StationSelector) -> Bool {
    return lhs.toInt() == rhs.toInt()
}
enum
定义之外,它将起作用


查看更多信息。

我花了一点时间试图使带有关联值的
枚举符合
哈希表

我将我的
enum
及其关联值与
Hashable
一致,这样它就可以被排序或用作
字典
键,或者做
Hashable
可以做的任何事情

您必须使关联值
enum
符合
Hashable
,因为关联值
enum
不能具有原始类型

public enum Components: Hashable {
    case None
    case Year(Int?)
    case Month(Int?)
    case Week(Int?)
    case Day(Int?)
    case Hour(Int?)
    case Minute(Int?)
    case Second(Int?)

    ///The hashValue of the `Component` so we can conform to `Hashable` and be sorted.
    public var hashValue : Int {
        return self.toInt()
    }

    /// Return an 'Int' value for each `Component` type so `Component` can conform to `Hashable`
    private func toInt() -> Int {
        switch self {
        case .None:
            return -1
        case .Year:
            return 0
        case .Month:
            return 1
        case .Week:
            return 2
        case .Day:
            return 3
        case .Hour:
            return 4
        case .Minute:
            return 5
        case .Second:
            return 6
        }

    }

}
还需要覆盖相等运算符:

/// Override equality operator so Components Enum conforms to Hashable
public func == (lhs: Components, rhs: Components) -> Bool {
    return lhs.toInt() == rhs.toInt()
}

只是为了强调塞扎之前说过的话。如果可以避免使用成员变量,则不需要实现equals运算符使枚举可散列–只需给它们一个类型

enum StationSelector : Int {
    case Nearest = 1, LastShown, List, Specific
    // automatically assigned to 1, 2, 3, 4
}
这就是你所需要的。现在,您还可以使用rawValue启动它们或稍后检索它

let a: StationSelector? = StationSelector(rawValue: 2) // LastShown
let b: StationSelector = .LastShown

if(a == b)
{
    print("Selectors are equal with value \(a?.rawValue)")
}

有关更多信息,请参见。

为了更具可读性,让我们使用Swift 3重新实现
StationSelector

enum StationSelector {
    case nearest, lastShown, list, specific(Int)
}

extension StationSelector: RawRepresentable {

    typealias RawValue = Int

    init?(rawValue: RawValue) {
        switch rawValue {
        case -1: self = .nearest
        case -2: self = .lastShown
        case -3: self = .list
        case (let value) where value >= 0: self = .specific(value)
        default: return nil
        }
    }

    var rawValue: RawValue {
        switch self {
        case .nearest: return -1
        case .lastShown: return -2
        case .list: return -3
        case .specific(let value) where value >= 0: return value
        default: fatalError("StationSelector is not valid")
        }
    }

}

Apple developer API参考说明了协议:

extension StationSelector: Equatable {

    static func == (lhs: StationSelector, rhs: StationSelector) -> Bool {
        return lhs.rawValue == rhs.rawValue
    }

}
extension StationSelector: Hashable {

    var hashValue: Int {
        return self.rawValue.hashValue
    }

}
当您定义没有关联值的枚举时,它会自动获得
Hashable
一致性,并且您可以通过添加单个
hashValue
属性,将
Hashable
一致性添加到其他自定义类型中

因此,由于
StationSelector
实现了关联的值,因此必须手动使
StationSelector
符合
Hashable
协议


第一步是实现
==
运算符,并使
站点选择器符合
均衡协议:

extension StationSelector: Equatable {

    static func == (lhs: StationSelector, rhs: StationSelector) -> Bool {
        return lhs.rawValue == rhs.rawValue
    }

}
extension StationSelector: Hashable {

    var hashValue: Int {
        return self.rawValue.hashValue
    }

}
用法:


一旦实施了
equalable
协议,您就可以使
StationSelector
符合
Hashable
协议:

extension StationSelector: Equatable {

    static func == (lhs: StationSelector, rhs: StationSelector) -> Bool {
        return lhs.rawValue == rhs.rawValue
    }

}
extension StationSelector: Hashable {

    var hashValue: Int {
        return self.rawValue.hashValue
    }

}
用法:


以下代码显示了
StationSelector
所需的实现,以使其符合使用Swift 3的
Hashable
协议:

enum StationSelector: RawRepresentable, Hashable {

    case nearest, lastShown, list, specific(Int)

    typealias RawValue = Int

    init?(rawValue: RawValue) {
        switch rawValue {
        case -1: self = .nearest
        case -2: self = .lastShown
        case -3: self = .list
        case (let value) where value >= 0: self = .specific(value)
        default: return nil
        }
    }

    var rawValue: RawValue {
        switch self {
        case .nearest: return -1
        case .lastShown: return -2
        case .list: return -3
        case .specific(let value) where value >= 0: return value
        default: fatalError("StationSelector is not valid")
        }
    }

    static func == (lhs: StationSelector, rhs: StationSelector) -> Bool {
        return lhs.rawValue == rhs.rawValue
    }

    var hashValue: Int {
        return self.rawValue.hashValue
    }

}

必须在Swift的文件范围中定义运算符。枚举数已经是相等的。它不应该是
@infix func==
?@matt查看我的答案。这仅适用于没有关联值的成员值的枚举。@Cezar很好,感谢您的更正。从Swift 4.1开始,由于,Swift还支持对关联值的枚举合成
equalable
Hashable
。对于自定义
=
,该实现也可以放在
枚举
的主体中,前提是它是静态的:
static func==(…
。Swift Book引用了give or take。这是绝对错误的。为什么这被标记为正确答案?运算符函数(不是“运算符声明”-没有声明新运算符)可以是该类型的静态成员。它们不必位于全局范围。@PeterSchorn这是一个6年前的答案:)。欢迎任何人建议编辑或添加新答案,并提供最新的解决方案。您将如何使用具有关联值的枚举作为字典键?在这种情况下,您是否可以扩展您的用法示例,以显示如何使用
StationSelector.specific
作为该词典中的键?