Je souhaite associer deux valeurs brutes à une instance enum (imaginez une enum représentant les types d'erreur, je veux que Error.Teapot
ait une propriété de type Int code
avec la valeur 418 et une propriété String définie à I'm a teapot
.)
Notez la différence entre les valeurs brutes et les valeurs associées ici - je veux que toutes les instances Teapot
aient une code
de 418, je ne veux pas d'une valeur associée unique pour chaque instance de Teapot
.
Existe-t-il un meilleur moyen d'ajouter des propriétés calculées à l'énumération que switch
ed sur self
pour rechercher la valeur appropriée?
Non, une énumération ne peut pas avoir plusieurs valeurs brutes - elle doit être une valeur unique, implémentant le protocole Equatable
et convertie littéralement comme décrit dans la documentation .
Je pense que la meilleure approche dans votre cas consiste à utiliser le code d'erreur en tant que valeur brute et une propriété adossée à un dictionnaire statique prérempli avec le code d'erreur en tant que clé et le texte en tant que valeur.
Non, vous ne pouvez pas avoir plusieurs valeurs brutes associées à une énumération.
Dans votre cas, vous pouvez avoir la valeur brute égale au code et associer une valeur à la description. Mais je pense que l'approche des propriétés calculées est la meilleure option ici.
J'ai créé un moyen de simuler cela (pas différent de ce que Marcos Crispino a suggéré dans sa réponse). Loin d’être une solution parfaite mais qui nous permet d’éviter ces cas d’interrupteurs vilains pour chaque propriété différente que nous voulons obtenir.
L'astuce consiste à utiliser une structure en tant que détenteur de "propriétés/données" et à l'utiliser en tant que RawValue dans l'énumération même.
Il y a un peu de duplication mais ça me sert bien jusqu'à présent. Chaque fois que vous souhaitez ajouter un nouveau cas d’énumération, le compilateur vous rappellera de remplir le cas supplémentaire dans le getter rawValue, ce qui devrait vous rappeler de mettre à jour le init?
, ce qui vous rappellerait de créer la nouvelle propriété statique sur la structure.
Code à la Gist:
enum VehicleType : RawRepresentable {
struct Vehicle : Equatable {
let name: String
let wheels: Int
static func ==(l: Vehicle, r: Vehicle) -> Bool {
return l.name == r.name && l.wheels == r.wheels
}
static var bike: Vehicle {
return Vehicle(name: "Bicycle", wheels: 2)
}
static var car: Vehicle {
return Vehicle(name: "Automobile", wheels: 4)
}
static var bus: Vehicle {
return Vehicle(name: "Autobus", wheels: 8)
}
}
typealias RawValue = Vehicle
case car
case bus
case bike
var rawValue: RawValue {
switch self {
case .car:
return Vehicle.car
case .bike:
return Vehicle.bike
case .bus:
return Vehicle.bus
}
}
init?(rawValue: RawValue) {
switch rawValue {
case Vehicle.bike:
self = .bike
case Vehicle.car:
self = .car
case Vehicle.bus:
self = .bus
default: return nil
}
}
}
VehicleType.bike.rawValue.name
VehicleType.bike.rawValue.wheels
VehicleType.car.rawValue.wheels
VehicleType(rawValue: .bike)?.rawValue.name => "Bicycle"
VehicleType(rawValue: .bike)?.rawValue.wheels => 2
VehicleType(rawValue: .car)?.rawValue.name => "Automobile"
VehicleType(rawValue: .car)?.rawValue.wheels => 4
VehicleType(rawValue: .bus)?.rawValue.name => "Autobus"
VehicleType(rawValue: .bus)?.rawValue.wheels => 8
Vous avez plusieurs options. Mais ni l'un ni l'autre ne comporte de valeurs brutes Les valeurs brutes ne sont tout simplement pas le bon outil pour la tâche.
Personnellement, je recommande fortement de ne pas avoir plus d'une valeur associée par cas enum. Les valeurs associées doivent être parfaitement évidentes (car elles n'ont pas d'arguments/noms), et en avoir plus d'un fouillis lourdement.
Cela dit, c'est quelque chose que le langage vous permet de faire. Cela vous permet également de définir chaque cas différemment, si vous en aviez besoin. Exemple:
enum ErrorType {
case teapot(String, Int)
case skillet(UInt, [CGFloat])
}
Les tuples sont une fonctionnalité intéressante de Swift, car ils vous permettent de créer des types ad-hoc. Cela signifie que vous pouvez le définir en ligne. Sucré!
Si chacun de vos types d'erreur va avoir un code et une description, vous pouvez alors avoir une propriété calculée info
(éventuellement avec un meilleur nom?). Voir ci-dessous:
enum ErrorType {
case teapot
case skillet
var info: (code: Int, description: String) {
switch self {
case .teapot:
return (418, "Hear me shout!")
case .skillet:
return (326, "I'm big and heavy.")
}
}
}
Appeler cela me serait beaucoup plus facile parce que vous pourriez utiliser une syntaxe de points savoureuse:
let errorCode = myErrorType.info.code
Une solution de contournement si vous vouliez avoir plusieurs propriétés statiques pour une erreur YourError pourrait être d’importer une liste de propriétés; vous pouvez définir l'objet racine dans un dictionnaire, avec votre valeur brute enum comme clé pour chaque objet, ce qui vous permet de récupérer facilement des données structurées statiques pour l'objet.
C’est un exemple d’importation et d’utilisation d’un plist: http://www.spritekitlessons.com/parsing-a-property-list-using-Swift/
Cela pourrait être excessif pour simplement une description d'erreur, pour laquelle vous pourriez simplement utiliser une fonction statique codée en dur avec une instruction switch pour vos valeurs enum, qui renvoie la chaîne d'erreur dont vous avez besoin. Il suffit de placer la fonction statique dans le même fichier .Swift que votre enum.
Par exemple,
static func codeForError(error : YourErrorType) -> Int {
switch(error) {
case .Teapot:
return "I'm a Teapot"
case .Teacup:
return "I'm a Teacup"
...
default:
return "Unknown Teaware Error"
}
}
Ceci a l'avantage (comparé à la solution .plist) d'une localisation plus accommodante. Cependant, une liste .plist pourrait simplement contenir une clé utilisée pour récupérer la localisation appropriée, à la place d'une chaîne d'erreur, à cette fin.
Dans les versions modernes de Swift, il est possible d'obtenir la valeur de chaîne d'un libellé de cas, même si sans cette énumération étant déclarée avec un : String
rawValue.
Comment obtenir le nom de la valeur d'énumération dans Swift?
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é.
Cela vous permet au moins d’avoir "plusieurs valeurs brutes" en ayant à la fois une valeur réelle : Int
rawValue ainsi que la chaîne utilisée comme étiquette de casse.
Cela ne répond pas particulièrement à votre question, qui demandait de trouver un meilleur moyen que switch
ing à travers self
de rechercher la valeur appropriée, mais cette réponse peut toujours être utile pour quelqu'un qui cherche dans le futur et qui a besoin d'un moyen simple d'obtenir une chaîne une énumération définie comme un type entier.
enum Error: UInt {
case Teapot = 418
case Kettle = 419
static func errorMessage(code: UInt) -> String {
guard let error = Error(rawValue: code) else {
return "Unknown Error Code"
}
switch error {
case .Teapot:
return "I'm a teapot!"
case .Kettle:
return "I'm a kettle!"
}
}
}
De cette façon, nous pouvons obtenir le message d'erreur errorMessage de deux manières:
rawValue
nous définissons pour l'énum)Option 1:
let option1 = Error.errorMessage(code: 418)
print(option1) //prints "I'm a teapot!"
Option 2:
let option2 = Error.errorMessage(code: Error.Teapot.rawValue)
print(option2) //prints "I'm a teapot!"
Pour commencer, en supposant que vous souhaitiez stocker un code et un message, vous pouvez utiliser une structure pour RawValue
struct ErrorInfo {
let code: Int
let message: String
}
L'étape suivante consiste à définir l'énumération comme étant RawRepresentable
et à utiliser ErrorInfo
comme valeur brute:
enum MyError: RawRepresentable {
typealias RawValue = ErrorInfo
case teapot
Il reste à mapper entre les instances de MyError
et ErrorInfo
:
static private let mappings: [(ErrorInfo, MyError)] = [
(ErrorInfo(code: 418, message: "I'm a teapot"), .teapot)
]
Avec ce qui précède, construisons la définition complète de l'énum:
enum MyError: RawRepresentable {
static private let mappings: [(ErrorInfo, MyError)] = [
(ErrorInfo(code: 418, message: "I'm a teapot"), .teapot)
]
case teapot
init?(rawValue: ErrorInfo) {
guard let match = MyError.mappings.first(where: { $0.0.code == rawValue.code && $0.0.message == rawValue.message}) else {
return nil
}
self = match.1
}
var rawValue: ErrorInfo {
return MyError.mappings.first(where: { $0.1 == self })!.0
}
}
Quelques notes: