web-dev-qa-db-fra.com

Comment accéder à une valeur associée Swift enum en dehors d'une instruction switch

Considérer:

enum Line {
    case    Horizontal(CGFloat)
    case    Vertical(CGFloat)
}

let leftEdge             =  Line.Horizontal(0.0)
let leftMaskRightEdge    =  Line.Horizontal(0.05)

Comment puis-je accéder directement à la valeur associée de lefEdge, sans utiliser une instruction switch?

let noIdeaHowTo          = leftEdge.associatedValue + 0.5

Cela ne compile même pas!

J'ai jeté un coup d'œil à ces DONC questions mais aucune des réponses ne semble résoudre ce problème.

La ligne non compilable noIdeaHowTo ci-dessus devrait vraiment être celle à une ligne, mais parce que la associated value peut être de n'importe quel type, je n'arrive même pas à voir comment le code utilisateur pourrait même écrire une méthode get ou genericValue "générique" dans le leum lui-même.

Je me suis retrouvé avec ça, mais c'est dégoûtant, et j'ai besoin de revoir le code à chaque fois que j'ajoute/modifie un cas ...

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

Un pointeur n'importe qui?

45
verec

Comme d'autres l'ont souligné, cela est désormais possible 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
65
verec

Je pense que vous essayez peut-être d'utiliser enum pour quelque chose auquel il n'était pas destiné. La façon d'accéder aux valeurs associées est en effet via switch comme vous l'avez fait, l'idée étant que le switch gère toujours chaque cas membre possible du enum.

Différents membres de enum peuvent avoir différentes valeurs associées (par exemple, vous pouvez avoir Diagonal(CGFloat, CGFloat) et Text(String) dans votre enum Line), vous devez donc toujours confirmer le cas auquel vous avez affaire avant de pouvoir accéder à la valeur associée. Par exemple, considérez:

enum Line {
    case Horizontal(CGFloat)
    case Vertical(CGFloat)
    case Diagonal(CGFloat, CGFloat)
    case Text(String)
}
var myLine = someFunctionReturningEnumLine()
let value = myLine.associatedValue // <- type?

Comment pourriez-vous supposer obtenir la valeur associée de myLine alors que vous pourriez avoir affaire à CGFloat, String ou twoCGFloats? C'est pourquoi vous avez besoin du switch pour découvrir d'abord quel case vous avez.

Dans votre cas particulier, il semble que vous feriez mieux d’utiliser un class ou struct pour Line, qui pourrait alors stocker le CGFloat et également avoir un enum propriété pour Vertical et Horizontal. Ou vous pouvez modéliser Vertical et Horizontal comme classes distinctes, avec Line étant un protocole (par exemple).

8
Arkku

Vous pouvez utiliser une instruction guard pour accéder à la valeur associée, comme ceci.

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)
6
Robert Muckle-Jones

Pourquoi cela n'est pas possible est déjà répondu, ce n'est donc qu'un conseil. Pourquoi ne l'implémentez-vous pas ainsi. Je veux dire que les énumérations et les structures sont les deux types de valeurs.

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)
3
DevAndArtist

Avec Swift 2, il est possible d'obtenir la valeur associée (lecture seule) en utilisant la réflexion.

Pour faciliter cela, ajoutez simplement le code ci-dessous à votre projet et étendez votre énumération avec le protocole EVAssociated.

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

Ensuite, vous pouvez accéder à la valeur .asociated avec un code comme celui-ci:

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

Le code ci-dessus provient de mon projet https://github.com/evermeer/EVReflectionhttps://github.com/evermeer/EVReflection

2
Edwin Vermeer