关于swift:通过哈希值访问枚举值?

Access an enum value by its hashvalue?

枚举具有名为"hashvalue"的属性,该属性是枚举内的索引。

现在我的问题是,是否可以使用数字访问其值?例:let variable:AnEnum = 0


如果要将枚举值映射为整数,则应直接使用原始值进行映射。例如(来自Swift编程语言:枚举):

1
2
3
4
5
enum Planet: Int {
    case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}

let possiblePlanet = Planet(rawValue: 7)

我不相信有任何文档承诺枚举的hashvalue是任何特别的东西(如果您有链接,我非常感兴趣)。如果没有,您应该在原始值的分配中显式地进行分配。


1
2
3
4
5
6
7
8
9
10
11
enum Opponent: String {
   case Player
   case Computer

   static func fromHashValue(hashValue: Int) -> Opponent {
       if hashValue == 0 {
           return .Player
       } else {
           return .Computer
       }
   }

}

说明:

由于无法从其hashvalue中获取枚举值,因此必须手动执行该操作。它不漂亮,但很管用。实际上,您创建了一个函数,它允许您传入所需值的索引,并手动将枚举值返回给调用方。这可能会让一个枚举与吨案件,但它像一个魅力为我工作。


Swift 4,iOS 12:

只需使用显式设置原始类型的枚举(如下面的示例中的Int):

1
2
3
4
5
6
enum OrderStatus: Int {
    case noOrder
    case orderInProgress
    case orderCompleted
    case orderCancelled
}

用途:

1
2
3
4
5
6
var orderStatus: OrderStatus = .noOrder // default value

print(orderStatus.rawValue) // it will print 0

orderStatus = .orderCompleted
print(orderStatus.rawValue) // it will print 2

事实上,对于swift 4.2,hashValue不再是enum中的索引。

编辑:刚刚找到了一个更安全的方法。您可以使用CaseIterable(swift 4.2)并在allCases集合中选择所需的案例。

1
2
3
4
5
6
7
8
9
10
11
12
13
enum Foo: CaseIterable {
  case bar
  case baz

  init?(withIndex index: Int) {
    guard Foo.allCases.indices ~= index else { return nil }
    self = Foo.allCases[index]
  }
}

Foo(withIndex: 0) // bar
Foo(withIndex: 1) // baz
Foo(withIndex: 2) // nil

注意:我把这个小把戏留在这里,因为玩不安全的指针很有趣,但是请不要用这个方法创建一个带有索引的案例。它依赖于快速的内存表示法,这种表示法可能会在没有通知的情况下发生更改,并且非常不安全,因为使用错误的索引会产生运行时错误。

也就是说,在swift中,使用原始内存中的Int来表示一个案例。所以您可以使用这个来构建一个使用不安全指针的案例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
enum Foo {
  case bar
  case baz

  init(withIndex index: UInt8) {
    var temp: Foo = .bar
    withUnsafeMutablePointer(to: &temp) { pointer in
      let ptr = UnsafeMutableRawPointer(pointer).bindMemory(to: UInt8.self, capacity: 1)
      ptr.pointee = index
    }

    self = temp
  }
}

Foo(withIndex: 0) // bar
Foo(withIndex: 1) // baz
Foo(withIndex: 2) // runtime error !

您的要求是让这一行代码工作,其中0是枚举变量的hashValue(注意,从xcode 10开始,0永远不是有效的hashvalue…):

1
let variable:AnEnum = 0

这只需使您的枚举ExpressibleByIntegerLiteralCaseIterable

1
2
3
4
5
6
7
extension AnEnum: CaseIterable, ExpressibleByIntegerLiteral {
    typealias IntegerLiteralType = Int

    public init(integerLiteral value: IntegerLiteralType) {
        self = AnEnum.allCases.first { $0.hashValue == value }!
    }
}

CaseIterable协议在Swift 4.2中是本机可用的,您可以使用https://stackoverflow.com/a/49588446/1033581中的代码自行实现旧的Swift版本(Swift 3.x,4.x)。


斯威夫特4.2??(基于@stephen paul之前的回答)

此答案使用switch而不是if/else子句。并返回可选的,因为不保证提供的哈希匹配。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
enum CellType:String{
    case primary,secondary,tierary
    /**
     * NOTE: Since there is no way to get back an enum value from its hashValue, you have to do it manually.
     * EXAMPLE: CellType.fromHashValue(hashValue: 1)?.rawValue//??primary
     */
    static func fromHashValue(hashValue: Int) -> CellType? {
        switch hashValue {
        case 0:
            return .primary
        case 1:
            return .secondary
        case 2:
            return .tierary
        default:
            return nil
        }
    }
}