如何对带参数的枚举进行 if-else 比较

Language : 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
}

有什么想法吗? 我并没有真正做什么,这个代码,我只是想知道,为什么我们不能做比较。

68333 次浏览

The answer is Equatable Protocol.

Now let's see how it works.

Consider this enum for example:

enum Barcode {
case upca(Int, Int)
case qrCode(String)
case none
}

If we check the equatable operator == on the enum it will fail.

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

How to fix this using Equatable Protocol?

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

The trick is to not actually check with == but rather use the case keyword in conjunction with a single = in your if statement. This is a little counter intuitive in the beginning but just like if let, you get used to it pretty fast:

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

The point is that in Swift you only get equation of enums for free if your enum uses a raw type or if you have no associated values (try it out, you can't have both at the same time). Swift however does not know how to compare cases with associated values - I mean how could it? Let's look at this example:

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...?

Maybe this makes it clearer why this cannot work out of the box:

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...?

So if you want enums with associated values, you'll have to implement Equatable protocol in your enum by yourself:

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