Si j'ai une énumération avec des valeurs brutes Integer
:
enum City: Int {
case Melbourne = 1, Chelyabinsk, Bursa
}
let city = City.Melbourne
Comment convertir une valeur city
en une chaîne Melbourne
? Ce type d'introspection de noms de types est-il disponible dans la langue?
Quelque chose comme (ce code ne fonctionnera pas):
println("Your city is \(city.magicFunction)")
> Your city is Melbourne
À partir de Xcode 7 beta 5 (Swift version 2), vous pouvez désormais imprimer les noms de types et les majuscules par défaut à l'aide de print(_:)
, ou les convertir en String
à l'aide de String
's init(_:)
initialiseur ou syntaxe d'interpolation de chaîne. Donc pour votre exemple:
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"
Il n’est donc plus nécessaire de définir et de maintenir une fonction pratique qui bascule sur chaque cas pour renvoyer un littéral de chaîne. De plus, cela fonctionne automatiquement pour toutes les énumérations, même si aucun type de valeur brute n'est spécifié.
debugPrint(_:)
& String(reflecting:)
peut être utilisé pour un nom complet:
debugPrint(city)
// prints "App.City.Melbourne" (or similar, depending on the full scope)
let cityDebugName = String(reflecting: city)
// cityDebugName contains "App.City.Melbourne"
Notez que vous pouvez personnaliser ce qui est imprimé dans chacun de ces scénarios:
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)"
(Je n'ai pas trouvé de moyen d'appeler cette valeur "par défaut", par exemple, pour imprimer "The city is Melbourne" sans avoir recours à une instruction switch. Utiliser \(self)
dans l'implémentation de description
/debugDescription
provoque une récursion infinie.)
Les commentaires ci-dessus _ Les initialiseurs de String
init(_:)
& init(reflecting:)
décrivent exactement ce qui est imprimé, en fonction de la conformité du type réfléchi:
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)
}
Voir le notes de publication pour plus d'informations sur ce changement.
Il n'y a pas d'introspection sur les cas d'enum pour le moment. Vous devrez les déclarer chacun manuellement:
enum City: String, CustomStringConvertible {
case Melbourne = "Melbourne"
case Chelyabinsk = "Chelyabinsk"
case Bursa = "Bursa"
var description: String {
get {
return self.rawValue
}
}
}
Si vous souhaitez que le type brut soit un Int, vous devez effectuer vous-même un changement:
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"
}
}
}
}
Dans Swift-3 (testé avec Xcode 8.1), vous pouvez ajouter les méthodes suivantes dans votre 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) }
}
Vous pouvez ensuite l'utiliser comme un appel de méthode normal sur votre instance enum. Cela pourrait également fonctionner dans les versions précédentes de Swift, mais je ne l’ai pas testé.
Dans votre exemple:
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"
Si vous souhaitez fournir cette fonctionnalité à tous vos enums, vous pouvez en faire une 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) }
}
}
Cela ne fonctionne que pour Swift enum.
Pour Objective-C enum
s, le seul moyen semble actuellement être, par exemple, d'étendre l'énumération avec CustomStringConvertible
pour aboutir à quelque chose comme:
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"
}
}
}
Et puis jetter la enum
comme String
:
String(UIDevice.currentDevice().batteryState)
En plus du support String (…) (CustomStringConvertible) pour les énumérations dans Swift 2.2, il existe également un support de réflexion cassé pour eux. Pour les cas enum avec des valeurs associées, il est possible d’obtenir l’étiquette du cas enum en utilisant la réflexion:
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"
En étant brisé, je voulais toutefois dire que pour les "simples" énumérations, la propriété calculée calculée ci-dessus label
renvoie simplement nil
(boo-hoo).
print(City.Chelyabinsk.label) // prints out nil
La situation avec réflexion devrait s'améliorer après Swift 3, apparemment. La solution pour l'instant est cependant String(…)
, comme suggéré dans l'une des autres réponses:
print(String(City.Chelyabinsk)) // prints out Cheylabinsk
C'est tellement décevant.
Pour le cas où vous avez besoin de ces noms (que le compilateur connaît parfaitement l’orthographe exacte, mais refuse de laisser l’accès - merci Swift team !! -) mais ne veut pas ou ne peut pas faire de String le base de votre enum, une alternative prolixe et encombrante est la suivante:
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()
}
}
}
Vous pouvez utiliser ce qui précède comme suit:
let elementType = ViewType.Title
let column = Column.Collections
let row = 0
println("fetching element \(elementType), column: \(column.string()), row: \(row)")
Et vous obtiendrez le résultat attendu (code de la colonne similaire, mais non affiché)
fetching element Title, column: Collections, row: 0
Dans ce qui précède, j'ai fait en sorte que la propriété description
renvoie à la méthode string
, mais il s'agit d'une question de goût. Notez également que les variables dites static
doivent être qualifiées de portée par le nom de leur type englobant, car le compilateur est trop amnésique et ne peut pas rappeler le contexte tout seul ...
L'équipe Swift doit vraiment être commandée. Ils ont créé un enum que vous ne pouvez pas utiliser enumerate
et que vous pouvez utiliser enumerate
sur sont des "séquences" mais pas enum
!
Pour 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"
}
}
}
si votre variable "batteryState", appelez:
self.batteryState.description
Swift a maintenant ce qu'on appelle valeur brute attribuée implicitement . Fondamentalement, si vous ne donnez pas de valeur brute à chaque cas et que l'énumération est de type String, elle en déduit que la valeur brute de la cas est elle-même au format chaîne. Allez l'essayer.
enum City: String {
case Melbourne, Chelyabinsk, Bursa
}
let city = City.Melbourne.rawValue
// city is "Melbourne"
Simple mais fonctionne ...
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")
}
}
L'initialiseur String(describing:)
peut être utilisé pour renvoyer le nom de l'étiquette de cas, même pour les énumérations avec des valeurs rawValues non-String:
enum Numbers: Int {
case one = 1
case two = 2
}
let one = String(describing: Numbers.one) // "one"
let two = String(describing: Numbers.two) // "two"
Notez que cela ne fonctionne pas si si l'enum utilise le modificateur @objc
:
Les interfaces Swift générées pour les types Objective-C n'incluent parfois pas le modificateur @objc
. Ces énumérations sont néanmoins définies dans Objective-C et ne fonctionnent donc pas comme ci-dessus.