Swift中非文字值的枚举

时间:2014-12-07 20:24:06

标签: swift enums

有没有办法将像字典元组这样的非文字值映射到枚举?以下代码将抛出 Raw value for enum must be literal

enum FileType {
    case VIDEO = ["name": "Video", "contentTypeMatcher": "video/"]
    case IMAGE = ["name": "Image", "contentTypeMatcher": "image/"]
    case AUDIO = ["name": "Audio", "contentTypeMatcher": "aduio/"]
    case PDF   = ["name": "PDF", "contentTypeMatcher":"application/pdf"]
    case TEXT  = ["name": "Text", "contentTypeMatcher": "text/"]
    case FOLDER= ["name": "Folder", "contentTypeMatcher" :"application/x-directory"]
    case PLAIN = ["name": "Plain", "contentTypeMatcher": ""]
}

当我使用元组时,它是一样的:

enum FileType {
    case VIDEO  = (name: "Video", contentTypeMatcher: "video/")
    case IMAGE  = (name: "Image", contentTypeMatcher: "image/")
    case AUDIO  = (name: "Audio", contentTypeMatcher: "aduio/")
    case PDF    = (name: "PDF", contentTypeMatcher:"application/pdf")
    case TEXT   = (name: "Text", contentTypeMatcher: "text/")
    case FOLDER = (name: "Folder", contentTypeMatcher :"application/x-directory")
    case PLAIN  = (name: "Plain", contentTypeMatcher: "")
}

3 个答案:

答案 0 :(得分:4)

语言参考在谈到Enumeration Declaration时,明确指出:

  

raw-value类型必须符合Equatable协议和以下文字可转换协议之一:IntegerLiteralConvertible用于整数文字,FloatingPointLiteralConvertible用于浮点文字,StringLiteralConvertible用于包含任意数量字符的字符串文字,ExtendedGraphemeClusterLiteralConvertible用于只包含单个字符的字符串文字。

除了文字之外别无其他可以用作原始值。

一种可行的解决方法是将字典表示为字符串 - 例如,您可以使用逗号分隔元素,使用冒号将值键入值:

enum FileType : String {
    case VIDEO = "name:Video,contentTypeMatcher:video/"
    case IMAGE = "name:Image,contentTypeMatcher:image/"
    ...
}

然后,使用计算属性(或您喜欢的方法),重建字典:

var dictValue: [String : String] {
    var dict = [String : String]()

    var elements = self.rawValue.componentsSeparatedByString(",")
    for element in elements {
        var parts = element.componentsSeparatedByString(":")
        if parts.count == 2 {
            dict[parts[0]] = parts[1]
        }
    }

    return dict
}

答案 1 :(得分:4)

@Antonio给出了解决方法,但没有回答实际问题。

定义你的枚举。

enum FileType {

    case Image, Video
}

根据RawRepresentable协议给出案例非文字值,无论您想要什么类型。通过枚举扩展来实现更清晰的代码。

extension FileType: RawRepresentable {

    typealias Tuple = (name: String, contentTypeMatcher: String)

    private static let allCases = [FileType.Image, .Video]

    // MARK: RawRepresentable

    typealias RawValue = Tuple

    init?(rawValue: Tuple) {

        guard let c = { () -> FileType? in

            for iCase in FileType.allCases {
                if rawValue == iCase.rawValue {
                    return iCase
                }
            }
            return nil

        }() else { return nil }
        self = c
    }

    var rawValue: Tuple {

        switch self {
        case .Image: return Tuple("Image", "image/")
        case .Video: return Tuple("Video", "video/")
        }
    }
}

为了能够在switch中匹配Tuple,请实现模式匹配运算符。

private func ~= (lhs: FileType.Tuple, rhs: FileType.Tuple) -> Bool {

    return lhs.contentTypeMatcher == rhs.contentTypeMatcher && lhs.name == rhs.name
}

就是这样......

let a = FileType.Image
print(a.rawValue.name) // "Image"
let b = FileType(rawValue: a.rawValue)!
print(a == b) // "true"
print(b.rawValue.contentTypeMatcher) // "image/"

我们说我毫无疑问地回答了这个问题。现在...... Enums(至少在Swift中)被设计为具有独特的案例。对这种解决方法的警告是,你可以(我希望偶然)为更多案例持有相同的rawValue。通常你的示例代码对我来说很有趣。除非你(由于非常合理的原因)需要从元组创建新的枚举值,否则请考虑重新设计。如果你想要使用这种解决方法,我建议(取决于项目)实现一些检查是否所有案例原始值都是唯一的。如果没有,请考虑一下:

enum FileType {

    case Video, Image

    var name: String {
        switch self {
        case .Image: return "Image"
        case .Video: return "Video"
    }

    var contentTypeMatcher: String {
        switch self {
        case .Image: return "image/"
        case .Video: return "video/"
    }
}

答案 2 :(得分:0)

我和我的同事最近一直在争论这个话题,因为Swifts枚举类型在其他语言中是唯一的。在像Java这样的语言中,枚举只是从Enumeration继承的类,您可以为每种情况分配 static 非文字值。

我们迅速找不到支持的方法。从Swift documentation

  

如果为每种枚举情况提供了一个值(称为“原始”值),则该值可以是字符串,字符或任何整数或浮点类型的值。

     

或者,枚举案例可以指定要存储的任何类型的关联值以及每个不同的案例值,这与其他语言中的并集或变体很相似。您可以将一组常见的相关案例定义为一个枚举的一部分,每个案例都有一组与之相关的不同类型的适当类型的值。

第二段似乎可以完成@Antonio要求的操作,但事实并非如此。在swift的示例中:

enum Barcode {
   case upc(Int, Int, Int, Int)
   case qrCode(String)
}

但是每个枚举都是一个具有不同值类型(元组和字符串)的实例,并且根据创建的每个枚举实例,其中的值是不同的。

我想要的东西不仅允许有限的原始值使用,而且每个枚举包含相同的值类型(例如,元组,对象等)。并且是静态的。

在我的同事们的帮助下,我们提出了两种折衷方案。

第一个是枚举的私有静态字典,其中包含您所需的值类型:

enum FooBarDict {
   case foo
   case bar

   private static let dict = [foo: (x: 42, y: "The answer to life, the universe, and everything"),
                              bar: (x: 420, y: "Party time")]

   var x: Int? { return FooBarDict.dict[self]?.x }
   var y: String? { return FooBarDict.dict[self]?.y }
}

此实现的问题是,在编译时无法确保开发人员已详尽地包括所有枚举实例。这意味着您所使用的任何属性都必须是可选的,或者返回默认值。

为解决该问题,我们提出了以下建议:

enum FooBarFunc {
    case foo
    case bar

    typealias Values = (x: Int, y: String)
    private func getValues() -> Values {
        switch self {
        case .foo: return (x: 42, y: "The answer to life, the universe, and everything")
        case .bar: return (x: 420, y: "Party time")
        }
    }

    var x: Int { return getValues().x }
    var y: String { return getValues().y }
}

由于getValues中的switch语句,它现在已经详尽无遗!如果不显式添加值类型,则开发人员无法添加新的大小写并进行编译。

我对这种方法的担心(也许是毫无根据的)是由于使用switch语句查找,这两种方法都可能较慢-尽管可以将其优化为与字典查找一样快。而且我不确定每次请求枚举属性时是否都会创建一个新值。我敢肯定我可以找到解决这两个问题的答案,但是我已经浪费了很多时间。

说实话,我希望我只是对语言有所了解,并且可以通过另一种方式轻松完成。