2015-07-11 10 views
35

Betrachten zuzugreifen:Wie ein Swift Enum zugehörige Wert außerhalb einer switch-Anweisung

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

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

Wie kann ich, sagen wir, lefEdge ‚s-Wert zugeordnet, direkt, ohne eine switch-Anweisung zu verwenden?

let noIdeaHowTo   = leftEdge.associatedValue + 0.5 

Dies wird nicht einmal kompiliert!

Ich habe mir theseSOquestions angesehen, aber keine der Antworten scheint dieses Problem zu beheben.

Die obige noIdeaHowTo nicht Compilierung Linie sollte wirklich sein, dass Einzeiler, sondern weil die jede Art sein kann, Ich kann nicht einmal sehen, wie Benutzercode schreiben könnte sogar eine „generische“ erhalten oder associatedValue Methode in le ENUM selbst .

endete ich damit, aber es ist gross und muss mir der Code jedes Mal zu überdenken ich hinzufügen/ändern Fall ...

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

Alle Zeiger anyone?

+0

Ich denke, was Sie getan haben, ist richtig (berechnete Eigenschaft, das ist). Enums können verknüpfte Werte eines beliebigen Typs und, was noch wichtiger ist, zählen. Ich sehe nicht, wie Apple Kurzzugriff auf sie bieten könnte, ohne uns in zahlreiche "as" und "if" zu zwingen. – courteouselk

+0

BTW, wenn Ihre enum CGFloat mit jedem Fall verknüpft ist, können Sie Rohwert anstelle von Assoziation verwenden. – courteouselk

+1

@AntonBronnikov Rohwerte sind konstant und müssen eindeutig sein, d. H. Sie können nicht zwei horizontale Instanzen mit einem anderen Wert haben. – Arkku

Antwort

42

Wie andere haben darauf hingewiesen, ist dies jetzt Art möglich in 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 
+1

Wie würden Sie das so implementieren, dass es für jedes Enum und jeden Fall funktioniert? –

+0

@RodrigoRuiz Ich empfehle Arkku die Antwort für Sie. Sie möchten eine Struktur, die eine Enumeration und einen Wert enthält, nicht nur eine Enumeration. – uliwitness

5

Ich denke, Sie könnten versuchen, enum für etwas zu verwenden, für das es nicht vorgesehen war. Die Art, auf die zugehörigen Werte zuzugreifen, ist in der Tat durch switch, wie Sie getan haben, die Idee ist, dass die switch immer behandelt jeden möglichen Mitglied Fall der enum.

Verschiedene Mitglieder der enum können verschiedene zugehörige Werte haben (beispielsweise Sie Diagonal(CGFloat, CGFloat) und Text(String) in Ihrem enum Line haben könnten), so müssen Sie immer bestätigen, welcher Fall, dass Sie sich zu tun, bevor Sie den zugehörigen Wert zugreifen können. Betrachten wir zum Beispiel:

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

Wie konnten Sie den zugehörigen Wert von myLine zu bekommen vermuten, wenn Sie mit CGFloat, String oder zweiCGFloat s tun haben könnte? Deshalb brauchen Sie die switch, um zuerst zu entdecken, welche case Sie haben.

In Ihrem speziellen Fall klingt es wie Sie mit einem class oder struct besser für Line sein könnte, die dann die CGFloat speichern kann und auch eine enum Eigenschaft für Vertical und Horizontal haben. Oder Sie könnten Vertical und Horizontal als separate Klassen modellieren, wobei Line ein Protokoll ist (zum Beispiel).

+0

Ja. Ich denke definitiv, dass dies Sprachunterstützung benötigen würde. Dies ist ein Fall, bei dem der Rückgabewert sich nicht nur auf verschiedene Typen, sondern auch auf unterschiedliche Arten beziehen sollte. Möglicherweise ein variierendes Zähl-Tupel wie in Fall x ... return (1) Fall y ... return (0, "stuff") Fall z ... return (-1, 3.14, ["für", "gut", "messen"]). Und das könnte besser sein, damit der Compiler direkt unterstützt. Riechen Sie ein Radar auf seinem Weg ... – verec

2

Warum dies nicht möglich ist ist bereits beantwortet, also ist dies nur ein Tipp. Warum implementierst du es nicht so? Ich meine, Enums und Strukturen sind beide Werttypen.

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

Mit Swift 2 ist es möglich, den zugehörigen Wert (nur lesen) erhalten Reflexion verwendet wird.

Um das zu erleichtern, fügen Sie einfach den folgenden Code zu Ihrem Projekt hinzu und erweitern Sie Ihre Enum mit dem EVA-assoziierten Protokoll.

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

Dann können Sie den .asociated Wert mit Code wie folgt zugreifen:

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

Der obige Code ist von meinem Projekt https://github.com/evermeer/EVReflection https://github.com/evermeer/EVReflection

0

Sie können eine Wache Anweisung die für den Zugriff auf assoziierter Wert, so.

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) 
Verwandte Themen