【问题标题】:In Swift, is it possible to convert a string to an enum?在 Swift 中,是否可以将字符串转换为枚举?
【发布时间】:2015-07-12 15:18:47
【问题描述】:

如果我有一个包含 a、b、c、d 情况的枚举,我可以将字符串“a”转换为枚举吗?

【问题讨论】:

  • 这些“转换”称为文字转换。

标签: swift enums


【解决方案1】:

当然。枚举可以有一个原始值。引用文档:

原始值可以是字符串、字符或任何整数或 浮点数类型

— 摘自:Apple Inc. “The Swift Programming Language”。电子书。 https://itun.es/us/jEUH0.l,

所以你可以使用这样的代码:

enum StringEnum: String 
{
    case one   = "value one"
    case two   = "value two"
    case three = "value three"
}

let anEnum = StringEnum(rawValue: "value one")!

print("anEnum = \"\(anEnum.rawValue)\"")

注意:您不需要在每个 case 之后写 = "one" 等。默认字符串值与案例名称相同,因此调用 .rawValue 只会返回一个字符串

编辑

如果您需要字符串值包含作为 case 值的一部分无效的空格等内容,那么您需要显式设置字符串。所以,

enum StringEnum: String 
{
  case one
  case two
  case three
}

let anEnum = StringEnum.one
print("anEnum = \"\(anEnum)\"")

给予

anEnum = "一个"

但如果您希望 case one 显示“值一”,则需要提供字符串值:

enum StringEnum: String 
{
  case one   = "value one"
  case two   = "value two"
  case three = "value three"
}

【讨论】:

  • 原始值必须是可字面转换的。您不能只使用任何 Hashable 类型。
  • 好的...我引用了 Apple 文档,其中列出了可以用作枚举原始值的值的类型。字符串,OP 的问题,是支持的类型之一。
  • 嗯,想象一下case one = "uno"。现在,如何将"one" 解析为枚举值? (不能使用 raws,因为它们用于本地化)
  • 也许你可以根据本地化在初始化时初始化原始字符串......或者只是为不同的本地化设置不同的枚举。在任何情况下,拥有枚举的全部目的是抽象出底层的原始数据,即本地化。一个好的代码设计不会将“uno”作为参数传递到任何地方,而是依赖于 StringEnum.one
  • 你不需要在每个案例之后写= "one"等。默认字符串值与案例名称相同。
【解决方案2】:

我用过这个:

public enum Currency: CaseIterable, Codable {
    case AFN = 971 // Afghani (minor=2)
    case DZD = 012 // Algerian Dinar (minor=2)

...

    private static var cachedLookup: [String: Currency] = [:]
    
    init?(string: String) {
        if Self.cachedLookup.isEmpty {
            Self.cachedLookup = Dictionary(uniqueKeysWithValues: Self.allCases.map { ("\($0)", $0) })
        }
        
        if let currency = Self.cachedLookup[string] {
            self = currency
            return
        } else {
            return nil
        }
    }
}

【讨论】:

    【解决方案3】:

    借鉴 djruss70 的答案以创建高度通用的解决方案:

    extension CaseIterable {
        static func from(string: String) -> Self? {
            return Self.allCases.first { string == "\($0)" }
        }
        func toString() -> String { "\(self)" }
    }
    

    用法:

    enum Chassis: CaseIterable {
        case pieridae, oovidae
    }
    
    let chassis: Chassis = Chassis.from(string: "oovidae")!
    let string: String = chassis.toString()
    

    注意:如果枚举被声明为@objc,这将很遗憾。据我所知,从 Swift 5.3 开始,除了蛮力解决方案(switch 语句)之外,没有办法让它与 @objc 枚举一起使用。

    如果有人碰巧知道一种方法可以使 @objc 枚举工作,我会对答案非常感兴趣。

    【讨论】:

      【解决方案4】:

      对于 Int 枚举及其 String 表示,我将枚举声明如下:

      enum OrderState: Int16, CustomStringConvertible {
      
          case waiting = 1
          case inKitchen = 2
          case ready = 3
      
          var description: String {
              switch self {
              case .waiting:
                  return "Waiting"
              case .inKitchen:
                  return "InKitchen"
              case .ready:
                  return "Ready"
              }
          }
      
          static func initialize(stringValue: String)-> OrderState? {
              switch stringValue {
              case OrderState.waiting.description:
                  return OrderState.waiting
              case OrderState.inKitchen.description:
                  return OrderState.inKitchen
              case OrderState.ready.description:
                  return OrderState.ready
      
              default:
                  return nil
              }
          }
      }
      

      用法:

      order.orderState = OrderState.waiting.rawValue
      
      let orderState = OrderState.init(rawValue: order.orderState)
      let orderStateStr = orderState?.description ?? ""
      print("orderStateStr = \(orderStateStr)")
      

      【讨论】:

        【解决方案5】:

        斯威夫特 4.2:

        public enum PaymentPlatform: String, CaseIterable {
            case visa = "Visa card"
            case masterCard = "Master card"
            case cod = "Cod"
        
            var nameEnum: String {
                return Mirror(reflecting: self).children.first?.label ?? String(describing: self)
            }
        
            func byName(name: String) -> PaymentPlatform {
                return PaymentPlatform.allCases.first(where: {$0.nameEnum.elementsEqual(name)}) ?? .cod
            }
        }
        

        【讨论】:

          【解决方案6】:

          在 Swift 4.2 中,CaseIterable 协议可用于带有 rawValues 的枚举,但字符串应与枚举大小写标签匹配:

          enum MyCode : String, CaseIterable {
          
              case one   = "uno"
              case two   = "dos"
              case three = "tres"
          
              static func withLabel(_ label: String) -> MyCode? {
                  return self.allCases.first{ "\($0)" == label }
              }
          }
          

          用法:

          print(MyCode.withLabel("one")) // Optional(MyCode.one)
          print(MyCode(rawValue: "uno"))  // Optional(MyCode.one)
          

          【讨论】:

          • 这是一个很好的答案!它实际上解决了这个问题。
          • 这是 OP 要求的唯一真正有效的答案,这是关于 case names 而不是原始值。好答案!
          • 虽然这行得通,但这是一件非常愚蠢的事情。请不要将功能基于代码中的案例名称。
          • 他还能做什么?如果他正在将枚举写入数据库然后需要将其转换回来怎么办?
          【解决方案7】:

          你只需要:

          enum Foo: String {
             case a, b, c, d
          }
          
          let a = Foo(rawValue: "a")
          assert(a == Foo.a)
          
          let ? = Foo(rawValue: "?")
          assert(? == nil)
          

          【讨论】:

          • 这在技术上不是正确的答案,因为它会检查原始值。在此处给出的示例中,没有指定原始值,因此它与案例名称隐式匹配,但如果您有一个带有原始值的枚举,则会中断。
          【解决方案8】:

          如果使用 Int 类型的枚举,您可以这样做:

          enum MenuItem: Int {
              case One = 0, Two, Three, Four, Five //... as much as needs
          
              static func enumFromString(string:String) -> MenuItem? {
                  var i = 0
                  while let item = MenuItem(rawValue: i) {
                      if String(item) == string { return item }
                      i += 1
                  }
                  return nil
              }
          }
          

          并使用:

          let string = "Two"
          if let item = MenuItem.enumFromString(string) {
              //in this case item = 1 
              //your code
          } 
          

          【讨论】:

          • 不能只使用语言中内置的类似功能,这太疯狂了。我可以想象您将值存储在 JSON 中,例如通过枚举名称,然后在解析时需要将它们转换回来。为您使用的每个枚举编写 enumFromString 方法似乎很疯狂。
          • @Peterdk,请提出最好的替代方案。 Igor 解决方案实际上对我有用。
          • @Hemang 它工作正常,好吧,但更好的解决方案是 Swift 支持自动执行此操作。为每个枚举手动执行此操作太疯狂了。但是,是的,这行得通。
          • @Peterdk,您能否为此添加单独的答案?它肯定会帮助这里的每个人。
          • Swift 本身不支持它并不疯狂。疯狂的是,功能依赖于类型的名称。当值更改时,您将不得不重构和重命名所有用法。这不是解决这个问题的正确方法。
          【解决方案9】:

          扩展 Duncan C 的答案

          extension StringEnum: StringLiteralConvertible {
          
              init(stringLiteral value: String){
                  self.init(rawValue: value)!
              }
          
              init(extendedGraphemeClusterLiteral value: String) {
                  self.init(stringLiteral: value)
              }
          
              init(unicodeScalarLiteral value: String) {
                  self.init(stringLiteral: value)
              }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2016-03-12
            • 2010-10-03
            • 1970-01-01
            • 2010-09-06
            • 2023-04-07
            • 2019-03-10
            相关资源
            最近更新 更多