【问题标题】:How to get a swift enum's associated value regardless of the enum case无论枚举大小写如何,如何快速获取枚举的关联值
【发布时间】:2014-11-10 21:53:52
【问题描述】:

我有一个对象 FormField,它有两个属性:一个字符串name,和一个可以接受任何类型的value——因此我把它做成了Any!。但是,a separate question 告诉我使用带有关联值的枚举而不是 Any!

enum Value {
    case Text(String!)
    case CoreDataObject(NSManagedObject!)
}

class FormField {
    var name: String
    var value: Value?
    // initializers...
}

然而,这种方法使得检查无效性变得非常冗长。如果我想为表单中的所有缺失字段显示警报视图,我将不得不在 switch 语句中对每个案例重复一次 nil 检查:

for field in self.fields {
    if let value = field.value {
        switch value {
        case .Text(let text):
            if text == nil {
                missingFields.append(field.name)
            }
        case .CoreDataObject(let object):
            if object == nil {
                missingFields.append(field.name)
            }
        }
    }
}

是否有更短的方法来访问枚举的关联值,而不管其类型如何?如果我将 FormField.value 设为 Any!,则上面的代码将很简单:

for field in self.fields {
    if field.value == nil {
        missingFields.append(field.name)
    }
}

【问题讨论】:

  • 您可以在case 语句中使用where 子句删除几行:case .Text(let text) where text == nil:
  • @MikeS 很棒的提示,谢谢。这样我就可以为每个可以为 nil 的情况设置一个 where 子句并让它失败。不过,我会用Any! 回去。使其成为枚举需要我编写过于冗长的代码。
  • 这不只是移动冗长吗?我假设在某些时候你必须知道那些Anys 的实际类型,然后你会做一堆if value is Stringif value is NSManagedObject 等等。
  • 另外,value 已经是您的 FormField 类中的可选内容。如果您只设置FormFieldvalue,当有一个时,您已经可以只检查if field.value == nil。这意味着您也不需要将您的 enum 的关联值隐式解包选项。
  • @MikeS 除非我将关联的值设为可选,否则我无法将它们与 nil 进行比较。编译器抱怨关联值的类型不符合NilLiteralConvertible

标签: ios cocoa cocoa-touch swift


【解决方案1】:

enum 中定义一个方法isMissing() - 只写一次。然后,您几乎可以得到您喜欢的东西:

for field in self.fields {
    if field.value.isMissing() {
        missingFields.append(field.name)
    }
}

看起来像这样(来自 Swift 解释器):

  1> class Foo {}
   >
  2> enum Value { 
  3.     case One(Foo!) 
  4.     case Two(Foo!) 
  5.      
  6.     func isMissing () -> Bool { 
  7.         switch self { 
  8.         case let .One(foo): return foo == nil 
  9.         case let .Two(foo): return foo == nil 
 10.         } 
 11.     } 
 12. }    
 13> let aVal = Value.One(nil)
aVal: Value = One {
  One = nil
}
 14> aVal.isMissing()
$R0: Bool = true

【讨论】:

  • 好主意!可能我会在其他情况下使用它。现在,对于关联值不是 nil 的情况,如果我有一个具有多个 UITextFields 的表单(即,它们都将持有),我无法确定它是哪个 FormField 对象的关联值.Text(String!) 类型的值和单独访问 String! 并不能告诉我它用于哪个字段。
  • @Cristik Hm... 我想当时我想要一些反射 API(例如,在这里看到 medium.com/@swiftthesorrow/reflection-in-swift-958824116b07
【解决方案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)
    }

上面的代码现在可以作为 cocoapod susbspec 在 https://github.com/evermeer/Stuff#enum 它还有一个很好的枚举扩展,用于枚举所有枚举值。

【讨论】:

    【解决方案3】:

    如果所有枚举案例的关联值都属于同一类型,则以下方法可能会有所帮助。

    enum Value {
        case text(NSString!), two(NSString!), three(NSString!) // This could be any other type including AnyClass
    }
    
    // Emulating "fields" datastruct for demo purposes (as if we had struct with properties).
    typealias Field = (en: Value, fieldName: String)
    let fields: [Field] = [(.text(nil),"f1"), (.two(nil), "f2"), (.three("Hey"), "f3")] // this is analog of "fields"
    
    let arrayOfFieldNamesWithEmptyEnums: [String] = fields.compactMap({
        switch $0.en {
        case let .text(foo), let .two(foo), let .three(foo): if foo == nil { return $0.fieldName } else { return nil }}
    })
    print("arrayOfFieldNamesWithEmptyEnums \(arrayOfFieldNamesWithEmptyEnums)")
    

    许多其他的东西也可以类似地获得。

    let arrayOfEnumsWithoutValues: [Value] = fields.compactMap({
        switch $0.en {
        case let .text(foo), let .two(foo), let .three(foo): if foo == nil { return $0.en } else { return nil }}
    })
    print("arrayOfEnumsWithoutValues \(arrayOfEnumsWithoutValues)")
    
    // just to check ourselves
    if let index = arrayOfEnumsWithoutValues.index(where: { if case .two = $0 { return true }; return false }) {
        print(".two found at index \(index)")
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-02-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多