【问题标题】:Access an enum value by its hashvalue?通过哈希值访问枚举值?
【发布时间】:2014-11-22 12:22:14
【问题描述】:

枚举有一个名为“hashValue”的属性,它是它在枚举中的索引。

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

【问题讨论】:

  • hashValue 只是从 Xcode 6 到 Xcode 9 的索引。在 Xcode 10 构建中,hashValue 与索引无关。换句话说:这个问题已经失去了它的价值。
  • 从文档中,很明显你不能依赖哈希值作为索引:“哈希值不能保证在你的程序的不同执行中是相等的。”

标签: swift enums


【解决方案1】:

如果你想将枚举值映射到整数,你应该直接使用原始值。例如(来自Swift Programming Language: Enumerations):

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

let possiblePlanet = Planet(rawValue: 7)

我不相信有任何文档承诺枚举的 hashValue 是什么特别的东西(如果你有链接,我很感兴趣)。如果没有,您应该明确分配原始值。

【讨论】:

  • 我的枚举是一个 CGPoints 的枚举。
  • 什么是“CGPoints 枚举”?
  • enum AnEnum:CGPoint {},抱歉我的选词不好,我想说“我的枚举是 CGPoint 类型的枚举”;)
  • 这还能编译吗?你应该得到“原始类型 'CGPoint' 不能从任何文字转换。”
  • 请不要...这确实使 IMO 枚举过多,并且可能会在边缘情况下产生许多奇怪的编译器错误。但是,如果您被限制在其中,请执行static func fromRaw(x: Int) -> AnEnum? 来做您想做的事情。 hashValue 什么都不承诺。
【解决方案2】:
enum Opponent: String {
   case Player
   case Computer

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

}

说明:

由于无法从其 hashValue 中取回枚举值,因此您必须手动进行。它不漂亮,但它有效。您实际上创建了一个函数,该函数允许您传入所需值的索引并手动将该枚举值返回给调用者。对于包含大量案例的枚举,这可能会令人讨厌,但它对我来说就像一个魅力。

【讨论】:

【解决方案3】:

Swift 4、iOS 12:

通过显式设置原始类型(如以下示例中的Int)来创建您的枚举:

enum OrderStatus: Int {
    case noOrder
    case orderInProgress
    case orderCompleted
    case orderCancelled
}

用法:

var orderStatus: OrderStatus = .noOrder // default value 

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

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

【讨论】:

    【解决方案4】:

    Swift 4.2 ?(基于@Stephen paul 之前的回答)

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

    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
            }
        }
    }
    

    【讨论】:

      【解决方案5】:

      您的要求是让这行代码正常工作,其中0 是枚举变量的hashValue(请注意,从 Xcode 10 开始,0 永远不是有效的 hashValue...):

      let variable:AnEnum = 0
      

      只需将枚举设为ExpressibleByIntegerLiteralCaseIterable即可:

      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)自己实现它。

      【讨论】:

        【解决方案6】:

        实际上,在 Swift 4.2 中,hashValue 不再是 enum 内的索引。

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

        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 内存表示,它可能会在没有通知的情况下发生变化,并且确实不安全,因为使用错误的索引会产生运行时错误。

        话虽如此,在 Swift 中,案例是使用原始内存中的 Int 表示的。所以你可以使用它来构建一个使用不安全指针的案例。

        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 !
        

        【讨论】:

          猜你喜欢
          • 2023-01-04
          • 2019-09-13
          • 2016-10-04
          • 2013-06-06
          • 1970-01-01
          • 2017-06-09
          • 2013-03-22
          • 2019-03-19
          • 1970-01-01
          相关资源
          最近更新 更多