values - switch self swift
Cómo acceder a un valor asociado de Swift enum fuera de una sentencia switch (5)
Como han señalado otros, esto ahora es posible en Swift 2:
import CoreGraphics
enum Line {
case Horizontal(CGFloat)
case Vertical(CGFloat)
}
let min = Line.Horizontal(0.0)
let mid = Line.Horizontal(0.5)
let max = Line.Horizontal(1.0)
func doToLine(line: Line) -> CGFloat? {
if case .Horizontal(let value) = line {
return value
}
return .None
}
doToLine(min) // prints 0
doToLine(mid) // prints 0.5
doToLine(max) // prints 1
Considerar:
enum Line {
case Horizontal(CGFloat)
case Vertical(CGFloat)
}
let leftEdge = Line.Horizontal(0.0)
let leftMaskRightEdge = Line.Horizontal(0.05)
¿Cómo puedo acceder, por ejemplo, lefEdge
valor asociado de lefEdge
, directamente, sin usar una instrucción switch?
let noIdeaHowTo = leftEdge.associatedValue + 0.5
¡Esto ni siquiera compila!
Eché un vistazo a these questions SO questions pero ninguna de las respuestas parece abordar este problema.
La línea de noIdeaHowTo
no compila arriba debería ser realmente de una sola línea, pero como el associated value
puede ser de cualquier tipo, ni siquiera puedo ver cómo el código de usuario podría escribir incluso un método "genérico" get o asociadoValue en leumum mismo.
Terminé con esto, pero es asqueroso, y necesito que vuelva a visitar el código cada vez que agregue / modifique un caso ...
enum Line {
case Horizontal(CGFloat)
case Vertical(CGFloat)
var associatedValue: CGFloat {
get {
switch self {
case .Horizontal(let value): return value
case .Vertical(let value): return value
}
}
}
}
Cualquier puntero a alguien?
Con Swift 2 es posible obtener el valor asociado (solo lectura) utilizando la reflexión.
Para hacerlo más fácil, simplemente agregue el código a continuación a su proyecto y amplíe su enumeración con el protocolo asociado a EVA.
public protocol EVAssociated {
}
public extension EVAssociated {
public var associated: (label:String, value: Any?) {
get {
let mirror = Mirror(reflecting: self)
if let associated = mirror.children.first {
return (associated.label!, associated.value)
}
print("WARNING: Enum option of /(self) does not have an associated value")
return ("/(self)", nil)
}
}
}
Luego puede acceder al valor asociado con un código como este:
class EVReflectionTests: XCTestCase {
func testEnumAssociatedValues() {
let parameters:[EVAssociated] = [usersParameters.number(19),
usersParameters.authors_only(false)]
let y = WordPressRequestConvertible.MeLikes("XX", Dictionary(associated: parameters))
// Now just extract the label and associated values from this enum
let label = y.associated.label
let (token, param) = y.associated.value as! (String, [String:Any]?)
XCTAssertEqual("MeLikes", label, "The label of the enum should be MeLikes")
XCTAssertEqual("XX", token, "The token associated value of the enum should be XX")
XCTAssertEqual(19, param?["number"] as? Int, "The number param associated value of the enum should be 19")
XCTAssertEqual(false, param?["authors_only"] as? Bool, "The authors_only param associated value of the enum should be false")
print("/(label) = {token = /(token), params = /(param)")
}
}
// See http://github.com/evermeer/EVWordPressAPI for a full functional usage of associated values
enum WordPressRequestConvertible: EVAssociated {
case Users(String, Dictionary<String, Any>?)
case Suggest(String, Dictionary<String, Any>?)
case Me(String, Dictionary<String, Any>?)
case MeLikes(String, Dictionary<String, Any>?)
case Shortcodes(String, Dictionary<String, Any>?)
}
public enum usersParameters: EVAssociated {
case context(String)
case http_envelope(Bool)
case pretty(Bool)
case meta(String)
case fields(String)
case callback(String)
case number(Int)
case offset(Int)
case order(String)
case order_by(String)
case authors_only(Bool)
case type(String)
}
El código anterior es de mi proyecto https://github.com/evermeer/EVReflection https://github.com/evermeer/EVReflection
Creo que puedes estar tratando de usar enum
para algo para lo que no estaba destinado. La forma de acceder a los valores asociados es a través del switch
como lo ha hecho, con la idea de que el switch
siempre maneje cada posible caso miembro de la enum
.
Diferentes miembros de la enum
pueden tener diferentes valores asociados (por ejemplo, podría tener Diagonal(CGFloat, CGFloat)
y Text(String)
en su enum Line
), por lo que siempre debe confirmar qué caso está tratando antes de poder acceder al valor asociado. Por ejemplo, considere:
enum Line {
case Horizontal(CGFloat)
case Vertical(CGFloat)
case Diagonal(CGFloat, CGFloat)
case Text(String)
}
var myLine = someFunctionReturningEnumLine()
let value = myLine.associatedValue // <- type?
¿Cómo podría suponer obtener el valor asociado de myLine
cuando podría estar CGFloat
con CGFloat
, String
o dos CGFloat
s? Es por eso que necesita el switch
para descubrir primero qué case
tiene.
En su caso particular, parece que podría estar mejor con una class
o struct
para Line
, que luego podría almacenar el CGFloat
y también tener una propiedad enum
para Vertical
y Horizontal
. O bien, podría modelar Vertical
y Horizontal
como clases separadas, siendo Line
un protocolo (por ejemplo).
Por qué esto no es posible ya está respondido, así que esto es solo un consejo. ¿Por qué no lo implementas así? Me refiero a las enumeraciones y las estructuras son ambos tipos de valores.
enum Orientation {
case Horizontal
case Vertical
}
struct Line {
let orientation : Orientation
let value : CGFloat
init(_ orientation: Orientation, _ value: CGFloat) {
self.orientation = orientation
self.value = value
}
}
let x = Line(.Horizontal, 20.0)
// if you want that syntax ''Line.Horizontal(0.0)'' you could fake it like this
struct Line {
let orientation : Orientation
let value : CGFloat
private init(_ orientation: Orientation, _ value: CGFloat) {
self.orientation = orientation
self.value = value
}
static func Horizontal(value: CGFloat) -> Line { return Line(.Horizontal, value) }
static func Vertical(value: CGFloat) -> Line { return Line(.Vertical, value) }
}
let y = Line.Horizontal(20.0)
Puede usar una declaración de guardia para acceder al valor asociado, así.
enum Line {
case Horizontal(Float)
case Vertical(Float)
}
let leftEdge = Line.Horizontal(0.0)
let leftMaskRightEdge = Line.Horizontal(0.05)
guard case .Horizontal(let leftEdgeValue) = leftEdge else { fatalError() }
print(leftEdgeValue)