如何在带参数的枚举类型中进行if-else比较

95

语言: Swift2.3

例如,让我展示不同种类的枚举

enum Normal {
    case one
    case two, three
}

enum NormalRaw: Int {
    case one
    case two, three
}

enum NormalArg {
    case one(Int)
    case two, three
}   

Switch可以在所有三个枚举类型上使用,示例如下:

var normal: Normal = .one
var normalRaw: NormalRaw = .one
var normalArg: NormalArg = .one(1)

switch normal {
    case .one: print("1")
    default: break
}

switch normalRaw {
    case .one: print(normalRaw.rawValue)
    default: break
}

switch normalArg {
    case .one(let value): print(value)
    default: break
}

关于if-else语句,我只能比较NormalNormalRaw,并且对于NormalArg会显示错误信息,所以我无法运行代码。

二进制运算符“==”不能应用于类型为NormalArg_的操作数

这是代码示例:

if normal == .two { // no issue
    .. do something
}

if normalRaw == .two { // no issue
    .. do something
}

if normalArg == .two { // error here (the above message)
    .. do something
}

if normalArg == .one(_) { // error here (the above message)
    .. do something
}

if normalArg == .three { // error here (the above message)
    .. do something
}

有什么想法吗?我并没有在这段代码中做任何事情,我只是在想为什么我们不能进行比较。

2个回答

211

要点在于实际上不使用 == 进行检查,而是在 if 语句中使用 case 关键字与单个 = 结合使用。这在开始时可能有些反直觉,但就像 if let 一样,你很快就会习惯:

enum Normal {
    case one
    case two, three
}

enum NormalRaw: Int {
    case one = 1
    case two, three
}

enum NormalArg {
    case one(Int)
    case two, three
}


let normalOne = Normal.one
let normalRawOne = NormalRaw.one
let normalArgOne = NormalArg.one(1)

if case .one = normalOne {
    print("A normal one") //prints "A normal one"
}

if case .one = normalRawOne {
    print("A normal \(normalRawOne.rawValue)") //prints "A normal 1"
}

if case .one(let value) = normalArgOne {
    print("A normal \(value)") //prints "A normal 1"
}

重点是,在Swift中,只有在您的枚举使用原始类型或者没有相关值时,才能免费获得枚举等式(试一试,您不能同时拥有两者)。然而,Swift不知道如何比较具有相关值的情况 - 我的意思是,它怎么可能知道呢?让我们看看这个例子:

Normal.one == .one //true
Normal.one == .two //false

NormalRaw.one == .one //true
NormalRaw.one == .two //false

NormalArg.one(1) == .one(1) //Well...?
NormalArg.one(2) == .one(1) //Well...?
NormalArg.one(1) == .two //Well...?

也许这就更清楚地说明了为什么它不能立即使用:

class Special {
    var name: String?
    var special: Special?
}

enum SpecialEnum {
    case one(Special)
    case two
}

var special1 = Special()
special1.name = "Hello"

var special2 = Special()
special2.name = "World"
special2.special = special1

SpecialEnum.one(special1) == SpecialEnum.one(special2) //Well...?

因此,如果您想要带有相关值的枚举,您将需要自己实现Equatable协议:

enum NormalArg: Equatable {
    case one(Int)
    case two

    static func ==(lhs: NormalArg, rhs: NormalArg) -> Bool {
        switch (lhs, rhs) {
        case (let .one(a1), let .one(a2)):
            return a1 == a2
        case (.two,.two):
            return true
        default:
            return false
        }
    }
}

2
我不知道我可以在if语句中添加case关键字。 - Zonily Jame
1
哦,抱歉,我可能误解了你的意思。问题在于,如果枚举类型有关联值(而原始值枚举类型有),Swift 默认不知道如何进行等式比较。如果您的枚举类型没有关联值或者它具有原始值类型,则可以通过标准库(我猜是通过 RawRepresentable 协议)免费获得等式比较。您肯定知道不能将带有关联值的枚举类型与原始类型混合使用,这就是为什么您无法比较“.one(Int) == .two”而不自己在协议上实现 Equatable 的原因。 - xxtesaxx
1
读起来(写起来)非常糟糕,但它能够正常工作。谢谢! - Womble
3
我不知道是谁想出了这个语法(如果情况是.option = enum),但它太糟糕了。IDE甚至无法猜测你要检查哪个枚举来给你选项,而如果你不知道此枚举的选项,你会不得不深入挖掘并浪费好几分钟的时间。 - Tamim Attafi
这个概念的Swift文档在这里 - Andrew Kirna
显示剩余2条评论

14
答案是可判等协议(Equatable Protocol)
现在让我们来看看它是如何工作的。
例如,考虑这个枚举:
enum Barcode {
    case upca(Int, Int)
    case qrCode(String)
    case none
}

如果我们在枚举上检查可比较运算符==,它将失败。

// Error: binary operator '==' cannot be applied to two Barcode operands
Barcode.qrCode("code") == Barcode.qrCode("code")

如何使用Equatable协议来修复这个问题?

extension Barcode: Equatable {
}

func ==(lhs: Barcode, rhs: Barcode) -> Bool {
    switch (lhs, rhs) {
    case (let .upca(codeA1, codeB1), let .upca(codeA2, codeB2)):
        return codeA1 == codeA2 && codeB1 == codeB2

    case (let .qrCode(code1), let .qrCode(code2)):
        return code1 == code2

    case (.None, .None):
        return true

    default:
        return false
    }
}

Barcode.qrCode("code") == Barcode.qrCode("code") // true
Barcode.upca(1234, 1234) == Barcode.upca(4567, 7890) // false
Barcode.none == Barcode.none // true

这是一个不错的修复方法,但我会选择xxtesaxx的答案,因为它更简单。 - Zonily Jame

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接