简体   繁体   English

关联值和原始值可以在 Swift 枚举中共存吗?

[英]Can associated values and raw values coexist in Swift enumeration?

There are examples on Swift book demonstrating associated values and raw values separately, is there a way to define enums with the two features together? Swift 书中有一些示例分别演示了关联值和原始值,有没有办法将这两个功能一起定义枚举?

I have tried to combine them, but got errors:我试图将它们结合起来,但出现错误:

enum Barcode :String {
    case UPCA(Int, Int, Int) = "Order 1" // Enum with raw type cannot have cases with arguments
    case QRCode(String) = "Order 2" // Enum with raw type cannot have cases with arguments
}

Yes this is possible.是的,这是可能的。 An enum may contain both associated values and raw values.枚举可能包含关联值和原始值。 ( Swift 5 & 4 ) (斯威夫特 5 & 4 )

Shorthand notation速记符号

The issue with your code is that you are using the shorthand notation for RawRepresentable and defining associated types.您的代码的问题在于您正在使用RawRepresentable简写符号定义关联类型。

Let's look at how to define these separately:让我们看看如何分别定义这些:

1) RawRepresentable (shorthand notation): 1) RawRepresentable (速记符号):

enum Barcode: String {
    case UPCA   = "order 1"
    case QRCode = "order 2"
}

2) Associated types: 2) 关联类型:

enum Barcode {
    case UPCA(Int, Int, Int)
    case QRCode(String)
}

Each of these is great, but what if you need both as your code snippet shows.这些中的每一个都很棒,但是如果您的代码片段显示两者都需要,该怎么办。

Solution解决方案

Define the enum with associated values and then implement conformance to RawRepresentable separately in an extension (ie not using shorthand notation).定义具有关联值的枚举,然后在扩展中单独实现对RawRepresentable一致性(即不使用速记符号)。

Example:例子:

enum Barcode {
    case UPCA(Int, Int, Int)
    case QRCode(String)
}

extension Barcode: RawRepresentable {

    public typealias RawValue = String

    /// Failable Initalizer
    public init?(rawValue: RawValue) {
        switch rawValue {
        case "Order 1":  self = .UPCA(1,1,1) 
        case "Order 2":  self = .QRCode("foo")
        default:
            return nil
        } 
    }

    /// Backing raw value
    public var rawValue: RawValue {
        switch self {
        case .UPCA:     return "Order 1"
        case .QRCode:   return "Order 2"
        }
    }

}

Minor Detail小细节

In this solution, defaults for the associated values, eg .UPCA(1,1,1) must be supplied when constructing the enum from the rawValue argument.在此解决方案中,当从 rawValue 参数构造枚举时,必须提供关联值的默认值,例如.UPCA(1,1,1) You can get fancy and use the associated types as part of the backing raw value — which is more powerful, but adds some complexity.您可以幻想并使用关联类型作为支持原始值的一部分——这更强大,但增加了一些复杂性。

References参考

For more info on the topic see Ole Begemann's excellent write up.有关该主题的更多信息,请参阅Ole Begemann 的优秀文章。

The answers here are great, but don't provide an alternative, so here is one:这里的答案很好,但没有提供替代方案,所以这里有一个:

I'm trying to write a convenient wrapper for Parse.com's rest API, and honestly this restriction imposed by swift made me write a bit more code, but the end result is more readable:我正在尝试为 Parse.com 的 rest API 编写一个方便的包装器,老实说,swift 强加的这个限制让我写了更多的代码,但最终结果更具可读性:

class Parse {

    enum Endpoint {
        case signUp(ParseHTTPBody)
        case login(ParseHTTPBody)
    }

}

extension Parse.Endpoint {

    var httpMethod: String {
        switch self {
        case .signUp, .login:
            return "POST"
        }
    }

    var path: String {
        switch self {
        case .signUp:
            return "/1/users"
        case .login:
            return "/1/login"
        }
    }
}

Notice, now I httpMethod and path instead of rawValue , which is more readable in my case:请注意,现在我使用httpMethodpath而不是rawValue ,这在我的情况下更具可读性:

func setParseEndpoint(endpoint: Parse.Endpoint) -> Self {

    URL = NSURL(string: baseURL + endpoint.path)
    HTTPMethod = endpoint.httpMethod

    return self
}

As of Swift 3 you can have both in one enum.从 Swift 3 开始,您可以在一个枚举中同时拥有两者。


Old answer:旧答案:

The error messages seem pretty clear: You have to pick one or the other.错误消息似乎很清楚:您必须选择其中之一。

I don't know how it works behind the scenes, so this is a guess, but it seems likely that the case arguments are stored as a tuple value where the "Raw Type" value would otherwise be stored我不知道它在幕后是如何工作的,所以这是一个猜测,但似乎 case 参数被存储为元组值,否则将存储“原始类型”值

As @Jiaaro already pointed out, you cannot do that (including in Beta5).正如@Jiaaro 已经指出的那样,您不能这样做(包括在 Beta5 中)。

However, it would make perfectly sense: an enum with attributed values could be implemented as a "discriminated union" or "variant" ( see also wiki "tagged union" ), where the "raw value" would take the role of the "tag".但是,这完全有道理:具有属性值的枚举可以实现为“有区别的联合”或“变体”(另请参见维基“标记联合” ),其中“原始值”将扮演“标记”的角色”。

That enum would then only take the space of the largest size of any attributed type plus the size of the tag (plus padding for alignment).然后,该枚举将只占用任何属性类型的最大大小加上标签大小(加上用于对齐的填充)的空间。

I solved it like this:我是这样解决的:

enum Barcode {

   case UPCA(Int, Int, Int)// = "Order 1"
   case QRCode(String)// = "Order 2"

   static func customRawValue(rawValue: String) -> Barcode? {

       switch rawValue {

       case "Order 1": return Barcode.UPCA(0, 0, 0)
       case "Order 2": return Barcode.QRCode("")
       default: return nil
       }
    }

    var customRawValue : String {

       switch self {
       case .UPCA: return "Order 1"
       case .QRCode: return "Order 2"
       }
    }
}

if let barcode = Barcode.customRawValue("Order 1") {

   print("Barcode found with custom rawValue: \(barcode)")

   print("Custom rawValue: \(barcode.customRawValue)")
}

It's somewhat hacky, but this solution worked great for my purpose!这有点hacky,但这个解决方案非常适合我的目的!

Elegant way to work with associated value ( even if the enum is indirect):使用关联值的优雅方式(即使枚举是间接的):

indirect enum MyEnum {
    var value: String? {
        return String(describing: self).components(separatedBy: "(").first
    }
    case greeting(text: String)
    case goodbye(bool: Bool)
    case hey
    case none
}

print(MyEnum.greeting(text: "Howdy").value)
// prints : greeting

now even you can use the value to implement Equatable like this:现在,您甚至可以使用该value来实现Equatable如下所示:

    indirect enum MyEnum: Equatable {
     static func == (lhs: MyEnum, rhs: MyEnum) -> Bool {
        lhs.value == rhs.value
     }
    
     var value: String? {
        return String(describing: self).components(separatedBy: "(").first
     }
     case greeting(text: String)
     case goodbye(bool: Bool)
     case hey
     case none
   }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM