Как получить имя значения перечисления в Swift?
Если у меня есть перечисление с необработанными значениями 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
Ответы
Ответ 1
Начиная с Xcode 7 beta 5 (Swift версии 2), теперь вы можете печатать имена типов и регистры по умолчанию, используя print(_:)
, или конвертировать в String
используя init(_:)
String
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
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' 'description'
/// * Otherwise, if 'T' conforms to 'CustomDebugStringConvertible',
/// the result is 'instance' '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' 'debugDescription'.
///
/// * Otherwise, if 'T' conforms to 'CustomStringConvertible', the result
/// is 'subject' '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)
}
Смотрите информацию о выпуске для получения информации об этом изменении.
Ответ 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"
}
}
}
}
Ответ 3
В 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) }
}
Затем вы можете использовать его как обычный вызов метода для вашего экземпляра enum. Это может также работать в предыдущих версиях 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
Для enum
Objective-C в настоящее время кажется, что единственным способом является, например, расширение перечисления с помощью 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)
Ответ 5
В верхней части поддержки String (...) (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
(boo-hoo).
print(City.Chelyabinsk.label) // prints out nil
Ситуация с отражением должна улучшаться после Swift 3, по-видимому. Решение на данный момент, однако, String(…)
, как предлагается в одном из других ответов:
print(String(City.Chelyabinsk)) // prints out Cheylabinsk
Ответ 6
Это так разочаровывает.
В случае, когда вам нужны эти имена (что компилятор прекрасно знает точное написание, но отказывается предоставить доступ - спасибо команде 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
on, это "Последовательности", но не enum
!
Ответ 7
Для Свифта:
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
Ответ 8
Swift теперь имеет так называемое Неявно присвоенное исходное значение. В принципе, если вы не даете исходные значения каждому случаю, а перечисление имеет тип String, оно выводит, что исходное значение case само по себе является строковым. Продолжайте, дайте ему попробовать.
enum City: String {
case Melbourne, Chelyabinsk, Bursa
}
let city = City.Melbourne.rawValue
// city is "Melbourne"
Ответ 9
Инициализатор String(describing:)
можно использовать для возврата имени метки регистра даже для перечислений с не-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"
Обратите внимание, что это не работает, если перечисление использует модификатор @objc
:
https://forums.swift.org/t/why-is-an-enum-returning-enumname-rather-than-caselabel-for-string-describing/27327
Сгенерированные интерфейсы Swift для типов Objective-C иногда не включают модификатор @objc
. Эти перечисления, тем не менее, определены в Objective-C и, следовательно, не работают, как описано выше.
Ответ 10
Простой, но работает...
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")
}
}