Как получить имя значения перечисления в Swift?


если у меня есть перечисление с raw 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
9 120

9 ответов:

начиная с Xcode 7 beta 5 Теперь вы можете печатать имена типов и случаи перечисления по умолчанию с помощью print(_:), или преобразовать в String используя String ' s init(_:) синтаксис инициализатора или Строковой интерполяции. Так что для вашего примера:

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 вызывает бесконечную рекурсию.)


Комментарии выше String ' s init(_:) & 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)
}


Смотрите заметки для получения информации об этом изменении.

на данный момент нет интроспекции по случаям перечисления. Вы должны будете объявить их каждый вручную:

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

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

Примечание: The Printable протокол в настоящее время не работает в детские площадки. Если вы хотите увидеть строку на игровой площадке, вам придется вызвать toRaw() вручную

Если вам нужен тип raw, чтобы быть Int, вам придется сделать переключатель самостоятельно:

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

  var description : String {
    get {
      switch(self) {
        case Melbourne:
          return "Melbourne"
        case Chelyabinsk:
          return "Chelyabinsk"
        case Bursa:
          return "Bursa"
      }
    }
  }
}

в Swift-3 (проверено с помощью XCode 8.1) вы можете добавить следующие методы в свой enum:

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

это работает только для быстрых перечислений.

Для 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)

это так обидно.

для случая, когда вам нужны эти имена (что компилятор прекрасно знает точное написание, но отказывается дать доступ-спасибо Swift team!! -- ) но не хотите или не можете сделать строку основой вашего перечисления, многословная, громоздкая альтернатива выглядит следующим образом:

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 on - это "последовательности", но не enum!

в верхней части строки ( ... ) (CustomStringConvertible) поддержка перечислений в Swift 2.2, есть также несколько сломанная поддержка отражения для них. Для случаев перечисления со связанными значениями можно получить метку случая перечисления с помощью отражения:

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 (хнык-хнык).

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

ситуация с отражением должна быть становится лучше после Swift 3, по-видимому. Решение на данный момент, хотя String(…), как было предложено в одном из других ответов:

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

простой, но работает...

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

Swift теперь имеет то, что известно как Неявно Присвоено Необработанное Значение. В основном, если вы не даете необработанные значения для каждого случая, а перечисление имеет тип String, он выводит, что необработанное значение случая само по себе находится в строковом формате. Перейти на дать ему попробовать.

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

let city = City.Melbourne.rawValue

// city is "Melbourne"

для 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.описание