如何访问一个斯威夫特枚举关联值switch语句之外

问题描述:

考虑:如何访问一个斯威夫特枚举关联值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

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

我想你可能会尝试使用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

为什么这是不可能的已经回答了,所以这只是一个建议。你为什么不像这样实施它。我的意思是枚举和结构都是值类型。

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) 

与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

您可以使用保护语句访问相关的价值,就像这样。

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)