Swift枚举中是否可以存在关联值和原始值?

时间:2014-06-11 20:16:28

标签: swift enums

Swift书中有一些例子分别展示了相关值和原始值,有没有办法用两种特征一起定义枚举?

我试图将它们合并,但却出错了:

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
}

6 个答案:

答案 0 :(得分:35)

是的,这是可能的。枚举可以包含关联值和原始值。 ( Swift 4& 3

速记符号

您的代码存在的问题是您使用简写表示法来RawRepresentable 定义关联类型。

让我们看看如何分别定义这些:

1)RawRepresentable(简写符号):

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

2)相关类型:

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

其中每个都很棒,但如果你的代码片段显示,那么该怎么办呢。

解决方案

使用关联值定义枚举,然后在扩展名中实现与RawRepresentable 单独的一致性(即不使用简写符号)。

示例:

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

}

次要细节

在此解决方案中,相关值的默认值,例如从rawValue参数构造枚举时必须提供.UPCA(1,1,1)。您可以使用相关类型作为支持原始值的一部分 - 这更强大,但会增加一些复杂性。

<强>参考

有关该主题的更多信息,请参阅Ole Begemann's优秀的写作。

答案 1 :(得分:13)

这里的答案很棒,但不提供替代方案,所以这里有一个:

我正在尝试为Parse.com的其余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"
        }
    }
}

注意,现在我httpMethodpath而不是rawValue,这在我的案例中更具可读性:

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

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

    return self
}

答案 2 :(得分:5)

从Swift 3开始,你可以同时使用一个枚举。


旧答案:

错误消息似乎非常清楚:您必须选择其中一个。

我不知道它在幕后是如何工作的,所以这是一个猜测,但似乎案例参数存储为一个元组值,其中&#34; Raw Type&#34;否则将存储值

答案 3 :(得分:4)

正如@Jiaaro已经指出的那样,你不能这样做(包括在Beta5中)。

然而,这将是非常有意义的:具有属性值的枚举可以实现为“受歧视的联合”或“变体”(see also wiki "tagged union"),其中“原始值”将扮演“标签”。

那个枚举只会占用任何属性类型的最大大小的空间加上标记的大小(加上对齐的填充)。

答案 4 :(得分:1)

我解决了这个问题:

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

这有些笨拙,但这个解决方案对我来说很有用!

答案 5 :(得分:0)

处理关联值的优雅方式(即使枚举是间接的):

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

现在,您甚至可以像这样使用 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
   }
相关问题