简体   繁体   English

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

[英]How to get the name of enumeration value in Swift?

If I have an enumeration with raw Integer values:如果我有一个带有原始Integer数值的枚举:

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

let city = City.Melbourne

How can I convert a city value to a string Melbourne ?如何将city值转换为字符串Melbourne Is this kind of a type name introspection available in the language?这种类型名称自省在语言中可用吗?

Something like (this code will not work):类似于(此代码不起作用):

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

As of Xcode 7 beta 5 (Swift version 2) you can now print type names and enum cases by default using print(_:) , or convert to String using String 's init(_:) initializer or string interpolation syntax.从 Xcode 7 beta 5(Swift 版本 2)开始,您现在可以默认使用print(_:)打印类型名称和枚举案例,或者使用Stringinit(_:)初始化程序或字符串插值语法转换为String So for your example:所以对于你的例子:

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"

So there is no longer a need to define & maintain a convenience function that switches on each case to return a string literal.所以不再需要定义和维护一个方便的函数来打开每个 case 以返回一个字符串文字。 In addition, this works automatically for any enum, even if no raw-value type is specified.此外,即使未指定原始值类型,这也会自动适用于任何枚举。

debugPrint(_:) & String(reflecting:) can be used for a fully-qualified name: 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"

Note that you can customise what is printed in each of these scenarios:请注意,您可以自定义在每个场景中打印的内容:

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

(I haven't found a way to call into this "default" value, for example, to print "The city is Melbourne" without resorting back to a switch statement. Using \\(self) in the implementation of description / debugDescription causes an infinite recursion.) (我还没有找到调用这个“默认”值的方法,例如,打印“The city is Melbourne”而不求助于 switch 语句。在description / debugDescription的实现中使用\\(self)会导致无限递归。)


The comments above String 's init(_:) & init(reflecting:) initializers describe exactly what is printed, depending on what the reflected type conforms to: 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)
}


See the release notes for info about this change.有关此更改的信息,请参阅发行说明

There is no introspection on enum cases at the moment.目前没有对枚举案例的内省。 You will have to declare them each manually:您必须手动声明它们:

enum City: String, CustomStringConvertible {
    case Melbourne = "Melbourne"
    case Chelyabinsk = "Chelyabinsk"
    case Bursa = "Bursa"

    var description: String {
        get {
            return self.rawValue
        }
    }
}

If you need the raw type to be an Int, you will have to do a switch yourself:如果您需要原始类型为 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"
      }
    }
  }
}

In Swift-3 (tested with Xcode 8.1) you can add the following methods in your enum:在 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) }
}

You can then use it as a normal method call on your enum instance.然后,您可以将其用作枚举实例上的普通方法调用。 It might also work in previous Swift versions, but I haven't tested it.它可能也适用于以前的 Swift 版本,但我还没有测试过。

In your example:在你的例子中:

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"

If you want to provide this functionality to all your enums, you can make it an extension:如果您想为所有枚举提供此功能,您可以将其设为扩展:

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

This only works for Swift enums.这仅适用于 Swift 枚举。

For Objective-C enum s the only way currently seems to be, for example, to extend the enum with CustomStringConvertible ending up with something like:对于 Objective-C enum s,目前似乎唯一的方法是,例如,使用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"
        }
    }
}

And then casting the enum as String :然后将enumString

String(UIDevice.currentDevice().batteryState)

The String(describing:) initializer can be used to return the case label name even for enums with non-String rawValues: String(describing:) describing String(describing:)初始值设定项可用于返回 case 标签名称,即使对于具有非 String rawValues 的枚举:

enum Numbers: Int {
    case one = 1
    case two = 2
}

let one = String(describing: Numbers.one) // "one"
let two = String(describing: Numbers.two) // "two"

Note that this does not work if the enum uses the @objc modifier:注意如果枚举使用,这并不工作@objc修改:

Generated Swift interfaces for Objective-C types sometimes do not include the @objc modifier.为 Objective-C 类型生成的 Swift 接口有时不包含@objc修饰符。 Those Enums are nevertheless defined in Objective-C, and thus do not work like above.然而,这些枚举是在 Objective-C 中定义的,因此不像上面那样工作。

On top of the String(…) (CustomStringConvertible) support for enums in Swift 2.2, there's also somewhat broken reflection support for them.除了 Swift 2.2 中对枚举的 String(...) (CustomStringConvertible) 支持之外,对它们的反射支持也有些破损。 For enum cases with associated values it is possible to get the label of the enum case using reflection:对于具有关联值的枚举案例,可以使用反射获取枚举案例的标签:

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"

By being broken, I however meant that for "simple" enums, the above reflection based label computed property just returns nil (boo-hoo).然而,通过被破坏,我的意思是对于“简单”枚举,上述基于反射的label计算属性仅返回nil (boo-hoo)。

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

The situation with reflection should be getting better after Swift 3, apparently.显然,在 Swift 3 之后,反射的情况应该会变得更好。 The solution for now though is String(…) , as suggested in one of the other answers:现在的解决方案是String(…) ,正如其他答案之一所建议的:

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

I bumped into this question and wanted to share a simple way to create the magicFunction mentioned我碰到了这个问题,想分享一个简单的方法来创建提到的magicFunction

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

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

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

This is so disappointing.这太令人失望了。

For the case when you need those names (that the compiler perfectly knows the exact spelling of, but refuses to let access -- thank you Swift team!! --) but do not want or cannot make String the base of your enum, a verbose, cumbersome alternative is as follows:如果您需要这些名称(编译器完全知道其确切拼写,但拒绝访问——谢谢 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()
        }
    }
}

You can use the above as follows:您可以按如下方式使用上述内容:

let elementType = ViewType.Title
let column = Column.Collections
let row = 0

println("fetching element \(elementType), column: \(column.string()), row: \(row)")

And you'll get the expected result (code for the Column similar, but not shown)你会得到预期的结果(列的代码类似,但未显示)

fetching element Title, column: Collections, row: 0

In the above, I have made the description property refer back to the string method, but this is a matter of taste.在上面,我已经让description属性引用回string方法,但这是一个品味问题。 Also note that so called static variables need to be scope qualified by the name of their enclosing type, as the compiler is too amnesic and cannot recall the context all by itself...另请注意,所谓的static变量需要通过其封闭类型的名称进行范围限定,因为编译器太健忘并且无法自行回忆上下文......

The Swift team must really be commanded. Swift 团队必须真正受到指挥。 They created enum that you cannot enumerate and that which you can use enumerate on are "Sequences" but not enum !他们创建了你不能enumerate ,你可以使用enumerate的是“序列”而不是enum

Swift now has what are known as Implicitly Assigned Raw Value . Swift 现在具有所谓的隐式分配原始值 Basically if you don't give raw values to each case and the enum is of type String, it deduces that the case's raw value is itself in string format.基本上,如果您不为每个 case 提供原始值并且枚举的类型为 String,则它会推断 case 的原始值本身就是字符串格式。 Go on give it a try.去试试吧。

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

let city = City.Melbourne.rawValue

// city is "Melbourne"

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

}

if your variable "batteryState" then call:如果您的变量“batteryState”然后调用:

self.batteryState.description

Simple but works...简单但有效...

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

Introspection in Swift Enums seems to work partially. Swift Enums 中的内省似乎部分起作用。

I saw @drewag's response, and found that an Enum with no rawValues can indeed have introspection in Swift 5.X with Xcode 11.5.我看到@drewag 的回复,发现没有 rawValues 的 Enum 确实可以在 Swift 5.X 和 Xcode 11.5 中进行自省。 This code works.此代码有效。

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)

Replace the "\\(self)" for "string" in the second Enum and you will get this printout: network 404 string在第二个Enum中将"\\(self)"替换为"string" ,您将得到以下打印输出:network 404 string

NOTE: Using String(self) instead of "\\(self)" in the first Enum will require the Enum to conform to the LosslessStringConvertible` protocol, and also add other initializers, so a string interpolation seems to be a good workaround.注意: "\\(self)" in the first Enum 中使用String(self)而不是"\\(self)" in the first will require the Enum to conform to the LosslessStringConvertible` 协议,并添加其他初始值设定项,因此字符串插值似乎是一个很好的解决方法。

To Add a var description: String to the enum, you will have to use a Switch statement will all the enum cases as pointed before要将var description: String添加到枚举中,您将必须使用 Switch 语句将所有枚举情况与之前指出的一样

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM