【问题标题】:How to get the name of enumeration value in Swift?如何在 Swift 中获取枚举值的名称?
【发布时间】:2014-07-29 13:42:30
【问题描述】:

如果我有一个带有原始 Integer 值的枚举:

enum City: Int {
  case Melbourne = 1, Chelyabinsk, Bursa
}

let city = City.Melbourne

如何将city 值转换为字符串Melbourne?这种类型名称自省在语言中可用吗?

类似的东西(此代码不起作用):

println("Your city is \(city.magicFunction)")
> Your city is Melbourne

【问题讨论】:

    标签: swift enumeration


    【解决方案1】:

    从 Xcode 7 beta 5(Swift 版本 2)开始,您现在可以默认使用 print(_:) 打印类型名称和枚举大小写,或者使用 Stringinit(_:) 初始化程序或字符串插值语法转换为 String .所以对于你的例子:

    enum City: Int {
        case Melbourne = 1, Chelyabinsk, Bursa
    }
    let city = City.Melbourne
    
    print(city)
    // prints "Melbourne"
    
    let cityName = "\(city)"   // or `let cityName = String(city)`
    // cityName contains "Melbourne"
    

    因此不再需要定义和维护一个方便的函数,该函数可以打开每种情况以返回字符串文字。此外,即使没有指定原始值类型,这也会自动适用于任何枚举。

    debugPrint(_:) & String(reflecting:) 可用于完全限定名称:

    debugPrint(city)
    // prints "App.City.Melbourne" (or similar, depending on the full scope)
    
    let cityDebugName = String(reflecting: city)
    // cityDebugName contains "App.City.Melbourne"
    

    请注意,您可以自定义在每个场景中打印的内容:

    extension City: CustomStringConvertible {
        var description: String {
            return "City \(rawValue)"
        }
    }
    
    print(city)
    // prints "City 1"
    
    extension City: CustomDebugStringConvertible {
        var debugDescription: String {
            return "City (rawValue: \(rawValue))"
        }
    }
    
    debugPrint(city)
    // prints "City (rawValue: 1)"
    

    (我还没有找到一种方法来调用这个“默认”值,例如,打印“The city is Melbourne”而不求助于 switch 语句。在实现中使用\(self) description/debugDescription 导致无限递归。)


    Stringinit(_:)init(reflecting:) 初始化器上面的 cmets 准确描述了打印的内容,具体取决于反射类型符合的内容:

    extension String {
        /// Initialize `self` with the textual representation of `instance`.
        ///
        /// * If `T` conforms to `Streamable`, the result is obtained by
        ///   calling `instance.writeTo(s)` on an empty string s.
        /// * Otherwise, if `T` conforms to `CustomStringConvertible`, the
        ///   result is `instance`'s `description`
        /// * Otherwise, if `T` conforms to `CustomDebugStringConvertible`,
        ///   the result is `instance`'s `debugDescription`
        /// * Otherwise, an unspecified result is supplied automatically by
        ///   the Swift standard library.
        ///
        /// - SeeAlso: `String.init<T>(reflecting: T)`
        public init<T>(_ instance: T)
    
        /// Initialize `self` with a detailed textual representation of
        /// `subject`, suitable for debugging.
        ///
        /// * If `T` conforms to `CustomDebugStringConvertible`, the result
        ///   is `subject`'s `debugDescription`.
        ///
        /// * Otherwise, if `T` conforms to `CustomStringConvertible`, the result
        ///   is `subject`'s `description`.
        ///
        /// * Otherwise, if `T` conforms to `Streamable`, the result is
        ///   obtained by calling `subject.writeTo(s)` on an empty string s.
        ///
        /// * Otherwise, an unspecified result is supplied automatically by
        ///   the Swift standard library.
        ///
        /// - SeeAlso: `String.init<T>(T)`
        public init<T>(reflecting subject: T)
    }
    


    有关此更改的信息,请参阅 release notes

    【讨论】:

    • 另外,如果你想要字符串值而不使用print(enum),你可以使用String(enum)
    • 重要提示,这 only 适用于 Swift 枚举。如果您将其标记为@objc 以允许在 OS X 上支持绑定,这将不起作用。
    • 很棒的 Swift 特定答案;但是,如果您需要在非 swift 枚举上执行此操作,例如在您的 locationManager didChangeAuthorizationStatus 委托回调中打印(目标 C)CLAuthorizationStatus 枚举的值,则需要定义协议扩展。例如:extension CLAuthorizationStatus: CustomStringConvertable { public var description: String { switch self { case .AuthorizedAlways: return "AuthorizedAlways" &lt;etc&gt; } } } - 完成此操作后,它应该可以按预期工作: print("Auth status: (\status))".
    • “截至 Xcode 7 beta 5”是没有意义的。定义这些的不是 Xcode,而是 Swift 编译器和 Swift 运行时库。我可以使用 Xcode 9.3,但我的代码仍然可以是 Swift 3,然后我将无法使用 Swift 4 功能。使用 Xcode 9.3,尽管 Xcode 9.3 比 Xcode 7 更新很多,但此代码不起作用。
    • 我的初始化程序 'init(_:)' 要求 City 在 xcode 10.2、Swift 5 上符合 'LosslessStringConvertible'。我们现在该怎么做?
    【解决方案2】:

    目前还没有对枚举案例进行自省。您必须手动声明它们:

    enum City: String, CustomStringConvertible {
        case Melbourne = "Melbourne"
        case Chelyabinsk = "Chelyabinsk"
        case Bursa = "Bursa"
    
        var description: String {
            get {
                return self.rawValue
            }
        }
    }
    

    如果您需要原始类型为 Int,则必须自己进行切换:

    enum City: Int, CustomStringConvertible {
      case Melbourne = 1, Chelyabinsk, Bursa
    
      var description: String {
        get {
          switch self {
            case .Melbourne:
              return "Melbourne"
            case .Chelyabinsk:
              return "Chelyabinsk"
            case .Bursa:
              return "Bursa"
          }
        }
      }
    }
    

    【讨论】:

    • 菜鸟问题,但是为什么放 get { return self.rawValue } 而不是 return self.value?我试过后者,效果很好。
    • 如果没有定义 setter,也可以省略 get { ... } 部分。
    • 感谢您的出色回答。在 Xcode 7.3 中,我得到:“Printable 已重命名为 CustomStringConvertible”。解决方案很简单——在上面的第一个代码示例中,将第一行更改为enum City : String, CustomStringConvertible {。作为 CSC 协议的一部分,您需要将属性更改为 public,例如:public var description : String {
    【解决方案3】:

    在 Swift-3(使用 Xcode 8.1 测试)中,您可以在枚举中添加以下方法:

    /**
     * The name of the enumeration (as written in case).
     */
    var name: String {
        get { return String(describing: self) }
    }
    
    /**
     * The full name of the enumeration
     * (the name of the enum plus dot plus the name as written in case).
     */
    var description: String {
        get { return String(reflecting: self) }
    }
    

    然后您可以将它用作枚举实例上的普通方法调用。 它可能也适用于以前的 Swift 版本,但我还没有测试过。

    在你的例子中:

    enum City: Int {
        case Melbourne = 1, Chelyabinsk, Bursa
        var name: String {
            get { return String(describing: self) }
        }
        var description: String {
            get { return String(reflecting: self) }
        }
    }
    let city = City.Melbourne
    
    print(city.name)
    // prints "Melbourne"
    
    print(city.description)
    // prints "City.Melbourne"
    

    如果您想为所有枚举提供此功能,您可以将其作为扩展:

    /**
     * Extend all enums with a simple method to derive their names.
     */
    extension RawRepresentable where RawValue: Any {
      /**
       * The name of the enumeration (as written in case).
       */
      var name: String {
        get { return String(describing: self) }
      }
    
      /**
       * The full name of the enumeration
       * (the name of the enum plus dot plus the name as written in case).
       */
      var description: String {
        get { return String(reflecting: self) }
      }
    }
    

    这仅适用于 Swift 枚举。

    【讨论】:

      【解决方案4】:

      String(describing:) 初始化器可用于返回 case 标签名称,即使对于具有非字符串 rawValues 的枚举也是如此:

      enum Numbers: Int {
          case one = 1
          case two = 2
      }
      
      let one = String(describing: Numbers.one) // "one"
      let two = String(describing: Numbers.two) // "two"
      

      请注意,如果枚举使用@objc 修饰符,则此不起作用

      为 Objective-C 类型生成的 Swift 接口有时不包含 @objc 修饰符。然而,这些枚举是在 Objective-C 中定义的,因此不能像上面那样工作。

      【讨论】:

      【解决方案5】:

      对于 Objective-C enums,目前唯一的方法似乎是,例如,用 CustomStringConvertible 扩展枚举,最终得到类似的东西:

      extension UIDeviceBatteryState: CustomStringConvertible {
          public var description: String {
              switch self {
              case .Unknown:
                  return "Unknown"
              case .Unplugged:
                  return "Unplugged"
              case .Charging:
                  return "Charging"
              case .Full:
                  return "Full"
              }
          }
      }
      

      然后将enum 转换为String

      String(UIDevice.currentDevice().batteryState)
      

      【讨论】:

        【解决方案6】:

        除了在 Swift 2.2 中对枚举的 String(...) (CustomStringConvertible) 支持之外,对它们的反射支持也有些破损。对于具有关联值的枚举案例,可以使用反射获取枚举案例的标签:

        enum City {
            case Melbourne(String)
            case Chelyabinsk
            case Bursa
        
            var label:String? {
                let mirror = Mirror(reflecting: self)
                return mirror.children.first?.label
            }
        }
        
        print(City.Melbourne("Foobar").label) // prints out "Melbourne"
        

        然而,我的意思是,对于“简单”枚举,上述基于反射的 label 计算属性只返回 nil (boo-hoo)。

        print(City.Chelyabinsk.label) // prints out nil
        

        显然,在 Swift 3 之后,反射的情况应该会好转。不过,目前的解决方案是String(…),正如其他答案之一所建议的那样:

        print(String(City.Chelyabinsk)) // prints out Cheylabinsk
        

        【讨论】:

        • 这似乎可以在 Swift 3.1 上运行,而无需使其成为可选:var label:String { let mirror = Mirror(reflecting: self); if let label = mirror.children.first?.label { return label } else { return String(describing:self) } }
        【解决方案7】:

        我碰到了这个问题,想分享一个创建提到的magicFunction的简单方法

        enum City: Int {
          case Melbourne = 1, Chelyabinsk, Bursa
        
            func magicFunction() -> String {
                return "\(self)"
            }
        }
        
        let city = City.Melbourne
        city.magicFunction() //prints Melbourne
        

        【讨论】:

          【解决方案8】:

          这太令人失望了。

          如果您需要这些名称(编译器完全知道确切的拼写,但拒绝允许访问——感谢 Swift 团队!!——)但不想或不能将 String 作为你的基础枚举,一个冗长、繁琐的替代方法如下:

          enum ViewType : Int, Printable {
          
              case    Title
              case    Buttons
              case    View
          
              static let all = [Title, Buttons, View]
              static let strings = ["Title", "Buttons", "View"]
          
              func string() -> String {
                  return ViewType.strings[self.rawValue]
              }
          
              var description:String {
                  get {
                      return string()
                  }
              }
          }
          

          您可以按如下方式使用上述内容:

          let elementType = ViewType.Title
          let column = Column.Collections
          let row = 0
          
          println("fetching element \(elementType), column: \(column.string()), row: \(row)")
          

          你会得到预期的结果(列的代码类似,但未显示)

          fetching element Title, column: Collections, row: 0
          

          在上面,我已经让description 属性引用回string 方法,但这只是个人喜好问题。另请注意,所谓的static 变量需要通过其封闭类型的名称进行范围限定,因为编译器过于健忘,无法自行调用上下文...

          必须真正指挥 Swift 团队。他们创建了您无法使用enumerate 的枚举,而您可以使用enumerate 的枚举是“序列”,但不是enum

          【讨论】:

          • 这似乎比在描述中做 return String(reflecting: self) 更冗长。
          【解决方案9】:

          Swift 现在拥有所谓的Implicitly Assigned Raw Value。基本上,如果您不为每个案例提供原始值并且枚举是字符串类型,那么它会推断出案例的原始值本身就是字符串格式。继续试一试。

          enum City: String {
            case Melbourne, Chelyabinsk, Bursa
          }
          
          let city = City.Melbourne.rawValue
          
          // city is "Melbourne"
          

          【讨论】:

            【解决方案10】:

            对于斯威夫特:

            extension UIDeviceBatteryState: CustomStringConvertible {
            
                public var description: String {
                    switch self {
                    case .unknown:
                        return "unknown"
                    case .unplugged:
                        return "unplugged"
                    case .charging:
                        return "charging"
                    case .full:
                        return "full"
                    }
                }
            
            }
            

            如果你的变量“batteryState”然后调用:

            self.batteryState.description
            

            【讨论】:

              【解决方案11】:

              简单但有效...

              enum ViewType : Int {
                  case    Title
                  case    Buttons
                  case    View
              }
              
              func printEnumValue(enum: ViewType) {
              
                  switch enum {
                  case .Title: println("ViewType.Title")
                  case .Buttons: println("ViewType.Buttons")
                  case .View: println("ViewType.View")
                  }
              }
              

              【讨论】:

                【解决方案12】:

                Swift Enums 中的自省似乎部分起作用。

                我看到@drewag 的回复,发现没有 rawValues 的 Enum 确实可以在带有 Xcode 11.5 的 Swift 5.X 中进行自省。此代码有效。

                public enum Domain: String {
                    case network
                    case data
                    case service
                    case sync
                    var description: String {
                        return "\(self)"     // THIS INTROSPECTION WORKS
                    }
                }
                enum ErrorCode: Int, CustomStringConvertible {
                    case success = 200
                    case created = 201
                    case accepted = 202
                    case badRequest = 400
                    case unauthorized = 401
                    case forbidden = 403
                    case notFound = 404
                    var code: Int {
                        return self.rawValue
                    }
                    var description: String {
                        return "\(self)"      //THIS DOES NOT WORK - EXEC_BAD_ACCESS
                    }
                }
                let errorCode = ErrorCode.notFound
                let domain = Domain.network
                print(domain.description, errorCode.code, errorCode.description)
                

                "\(self)" 替换为第二个"string" 中的Enum,您将得到以下打印输出: 网络404字符串

                注意:使用 String(self) 代替 "\(self)" in the first Enumwill require the Enum to conform to theLosslessStringConvertible` 协议,并添加其他初始化程序,因此字符串插值似乎是一个很好的解决方法。

                要将var description: String 添加到枚举中,您必须使用 Switch 语句来处理前面指出的所有枚举情况

                var description: String {
                    switch self {
                    case .success: return "Success"
                    case .created: return "Created"
                    case .accepted: return "Accepted"
                    }
                }
                

                【讨论】:

                • "\(self)" 在您的 ErrorCode 枚举中应该可以工作。你得到一个 EXEC_BAD_ACCESS 的原因是因为description 上的无限递归。如果您删除 var description 实现并单独编写类似 print("\(ErrorCode.success)") 的内容,您将能够很好地获得枚举案例名称。
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2013-04-08
                • 2012-12-28
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多