如何在Swift中获取枚举值的名称?


167

如果我有一个带有原始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

Answers:


139

从Xcode 7 beta 5(Swift版本2)开始,您现在可以默认使用来打印类型名称和枚举大小写print(_:),或者String使用Stringinit(_:)初始值设定项或字符串插值语法将其转换为。因此,对于您的示例:

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)"

(我还没有找到一种方法来调用此“默认”值,例如,在不诉诸switch语句的情况下打印“城市是墨尔本”。\(self)description/ 的实现中使用debugDescription会导致无限递归。)


上面Stringinit(_:)init(reflecting:)初始值设定项中的注释确切描述了打印的内容,具体取决于所反映的类型符合以下条件:

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


有关此更改的信息, 请参见发行说明


8
另外,如果您不希望使用字符串值,则print(enum)可以使用String(enum)
Kametrixom

44
重要的收获,这适用于Swift枚举。如果将其标记为@objc以允许在OS X上进行绑定支持,则将无法使用。
克劳斯·约根森

11
特定于Swift的出色答案;但是,如果您需要在非swift枚举上执行此操作,例如CLAuthorizationStatuslocationManager didChangeAuthorizationStatus委托回调中打印(Objective C)枚举的值,则需要定义协议扩展。例如:extension CLAuthorizationStatus: CustomStringConvertable { public var description: String { switch self { case .AuthorizedAlways: return "AuthorizedAlways" <etc> } } }-完成此操作后,它应会按预期工作:print(“ Auth status:(\ status))”。
杰夫罗

3
“自Xcode 7 beta 5起”是没有意义的。不是Xcode定义了其中的任何一个,而是Swift编译器和Swift运行时库。我可以使用Xcode 9.3,但我的代码仍可以是Swift 3,然后我将无法使用Swift 4功能。使用Xcode 9.3,尽管Xcode 9.3比Xcode 7更新得多,该代码也无法工作
。– Mecki,

8
我得到了初始化程序“ init(_ :)”,要求City符合Swift 5的xcode 10.2上的“ LosslessStringConvertible”。
rockgecko

73

目前没有对枚举案例的自省。您将必须分别手动声明它们:

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

2
Noob问题,但是为什么要把get {return self.rawValue}而不是仅仅返回self.value呢?我尝试了后者,并且效果很好。
Chuck Krutsinger

get { ... }如果您未定义设置器,也可以为简洁起见省略该部分。
iosdude '02

1
感谢您的出色回答。在Xcode 7.3中,我得到:“可打印已重命名为CustomStringConvertible”。解决方案很容易-在上面的第一个代码示例中,将第一行更改为enum City : String, CustomStringConvertible {。作为CSC协议的一部分,您然后需要将属性更改为public,例如:public var description : String {
Jeffro

44

在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枚举。


18

对于Objective-C来说enum,当前唯一的方法似乎是例如以以下方式扩展枚举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"
        }
    }
}

然后将enumas 转换为String

String(UIDevice.currentDevice().batteryState)

12

String(describing:)初始化可以用来甚至对于非字符串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修饰符:

https://forums.swift.org/t/why-is-an-enum-returning-enumname-rather-than-caselabel-for-string-describing/27327

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


7

除了对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

2
这似乎可以在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) } }
David James

5

真令人失望。

对于这种情况,当您需要这些名称(编译器完全知道其确切拼写,但拒绝让其访问-谢谢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)")

您将获得预期的结果(类似于Column的代码,但未显示)

fetching element Title, column: Collections, row: 0

在上面,我做了 description属性重新引用该string方法,但这只是一个问题。还请注意,所谓的static变量需要通过其封闭类型的名称来限定范围,因为编译器过于健忘并且无法单独调用所有上下文...

斯威夫特团队必须真正受到指挥。他们创建了您无法enumerate使用的枚举,并且您可以使用的枚举enumerate是“序列”,但不是enum


这似乎很漫长,而不是仅仅在描述中返回return String(reflecting:self)。
恩恩

4

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

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

    func magicFunction() -> String {
        return "\(self)"
    }
}

let city = City.Melbourne
city.magicFunction() //prints Melbourne

3

Swift现在具有所谓的隐式分配原始值。基本上,如果您不为每种情况提供原始值,并且枚举的类型为String,则可以推断出案例的原始值本身就是字符串格式。继续尝试。

enum City: String {
  case Melbourne, Chelyabinsk, Bursa
}

let city = City.Melbourne.rawValue

// city is "Melbourne"

3

对于Swift:

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

1

简单但可行...

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")
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.