2015-07-11 78 views
35

考慮:如何訪問一個斯威夫特枚舉關聯值switch語句之外

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

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

如何訪問,也就是說,相關值,直接lefEdge的,不使用switch語句?

let noIdeaHowTo   = leftEdge.associatedValue + 0.5 

這甚至沒有編譯!

我看了一下theseSOquestions但沒有一個答案似乎解決了這個問題。

noIdeaHowTo上面的非編譯行應該是一行代碼,但因爲可以是任何類型,所以我甚至無法看到用戶代碼在le enum本身中如何編寫「generic」get或associatedValue方法。

我結束了這一點,但它是毛,而需要我每次我添加/修改的情況下,時間再看看代碼...

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

任何指針的人?

+0

我認爲你所做的是正確的(計算屬性,即)。枚舉可以有任何類型的關聯值,更重要的是任何數量。我沒有看到蘋果如何在不迫使我們陷入無數「as」和「if」的情況下提供速記訪問。 – courteouselk

+0

順便說一句,如果您的枚舉具有與每種情況關聯的CGFloat,則可以考慮使用原始值而不是關聯。 – courteouselk

+1

@AntonBronnikov原始值是常量,它們必須是唯一的,即不能有兩個具有不同值的「水平」實例。 – Arkku

回答

42

正如其他人所指出的那樣,這是目前在斯威夫特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

你會如何實現它的方式,它適用於任何枚舉和任何情況? –

+0

@RodrigoRuiz我向你推薦Arkku的回答。你想要一個包含一個枚舉和一個值的結構,而不僅僅是一個枚舉。 – uliwitness

5

我想你可能會嘗試使用enum來實現它不打算用於的目的。通過switch的方式來訪問關聯的值的方法的確如此,因爲switch總是處理每個可能的成員案例enum。在enum

不同成員可以具有不同的相關值(例如,你可以在你的enum LineDiagonal(CGFloat, CGFloat)Text(String)),所以你必須確認你正在處理,然後才能訪問相關值的情況。例如,考慮:

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

你怎麼竟敢從myLine獲得相關值時,你可能會處理CGFloatString,或 CGFloat S'這就是爲什麼你需要switch首先發現你有哪些case

在您的特定情況下,它聽起來就像你可能是一個classstructLine,那麼這可能會存儲CGFloat,也有一個enum屬性VerticalHorizontal更好。或者,您可以將VerticalHorizontal作爲單獨的類別進行建模,其中Line是一個協議(例如)。

+0

是。我絕對認爲這需要語言支持。這是一種情況,其中回報值不應該是關於變化的類型,而是不同的元數。返回(1)case y ... return(0,「stuff」)case z ... return(-1,3.14,[「for」,「good」, 「測量」])。編譯器直接支持這可能更好。嗅探雷達的方式... – verec

2

爲什麼這是不可能的已經回答了,所以這只是一個建議。你爲什麼不像這樣實施它。我的意思是枚舉和結構都是值類型。

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

與SWIFT 2有可能獲得相關的值使用反射(只讀)。

爲了簡化操作,只需將下面的代碼添加到項目中,並使用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) 
      } 
     } 
    } 

然後你就可以使用代碼訪問.asociated值是這樣的:

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

上面的代碼是從我的項目https://github.com/evermeer/EVReflection https://github.com/evermeer/EVReflection

0

您可以使用保護語句訪問相關的價值,就像這樣。

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)