如何在 Swift 中忽略枚举与关联值的关联值来比较枚举?

在阅读 如何测试 Swift 枚举与相关值的相等性之后,我实现了以下枚举:

enum CardRank {
case Number(Int)
case Jack
case Queen
case King
case Ace
}


func ==(a: CardRank, b: CardRank) -> Bool {
switch (a, b) {
case (.Number(let a), .Number(let b))   where a == b: return true
case (.Jack, .Jack): return true
case (.Queen, .Queen): return true
case (.King, .King): return true
case (.Ace, .Ace): return true
default: return false
}
}

下列守则适用:

let card: CardRank = CardRank.Jack
if card == CardRank.Jack {
print("You played a jack!")
} else if card == CardRank.Number(2) {
print("A two cannot be played at this time.")
}

However, this doesn't compile:

let number = CardRank.Number(5)
if number == CardRank.Number {
print("You must play a face card!")
}

它会给出以下错误信息:

Binary operator '==' cannot be applied to operands of type 'CardRank' and '(Int) -> CardRank'

我假设这是因为它期望一个完整的类型,而 CardRank.Number没有指定一个完整的类型,而 CardRank.Number(2)指定了。但是,在这种情况下,我希望它匹配 任何数字; 而不仅仅是一个特定的数字。

显然,我可以使用 switch 语句,但是实现 ==操作符的全部目的是为了避免这种冗长的解决方案:

switch number {
case .Number:
print("You must play a face card!")
default:
break
}

有没有办法在忽略枚举的关联值的同时将枚举与关联值进行比较?

注意: 我意识到我可以将 ==方法中的 case 改为 case (.Number, .Number): return true,但是,尽管它会正确地返回 true,我的比较看起来仍然像是与一个特定的数字(number == CardRank.Number(2); 其中2是一个虚值)而不是 any数字(number == CardRank.Number)进行比较。

36799 次浏览

Edit: As Etan points out, you can omit the (_) wildcard match to use this more cleanly:

let number = CardRank.Number(5)
if case .Number = number {
// Is a number
} else {
// Something else
}

不幸的是,我认为没有比 Swift 1.2中的 switch方法更简单的方法了。

然而,在 Swift 2中,您可以使用新的 if-case模式匹配:

let number = CardRank.Number(5)
if case .Number(_) = number {
// Is a number
} else {
// Something else
}

如果希望避免冗长,可以考虑向实现 switch 语句的枚举添加一个 isNumber计算属性。

Unfortunately in Swift 1.x there isn't another way so you have to use switch which isn't as elegant as Swift 2's version where you can use if case:

if case .Number = number {
//ignore the value
}
if case .Number(let x) = number {
//without ignoring
}

这里有一个更简单的方法:

enum CardRank {
case Two
case Three
case Four
case Five
case Six
case Seven
case Eight
case Nine
case Ten
case Jack
case Queen
case King
case Ace


var isFaceCard: Bool {
return (self == Jack) || (self == Queen) || (self == King)
}
}

不需要重载 = = 运算符,检查卡片类型也不需要混乱的语法:

let card = CardRank.Jack


if card == CardRank.Jack {
print("You played a jack")
} else if !card.isFaceCard {
print("You must play a face card!")
}

In Swift 4.2 Equatable will be synthesized if all your associated values conform to Equatable. All you need to do is add Equatable.

enum CardRank: Equatable {
case Number(Int)
case Jack
case Queen
case King
case Ace
}

Https://developer.apple.com/documentation/swift/equatable?changes=_3

你不需要 func ==Equatable。只需使用 枚举案例模式枚举案例模式

let rank = CardRank.Ace
if case .Ace = rank { print("Snoopy") }

我不想遵循 Equatable(它对我也没有帮助) ,我想过滤其他情况,而不是一个特定的,所以,而不是简单地写 card != .Number,我必须写下面的内容。(我根据这个问题调整了我的代码。)

enum CardRank {
...
var isNumber: Bool {
if case .Number = self { return true }
return false
}
}

因此,我可以在一个复杂的条件下编写 没有号码:

if something && !card.isNumber { ... }

我希望我可以只写 card != .Number,但编译器总是抱怨与 如果没有更多的上下文,表达式的类型是不明确的。也许在即将到来的快速版本!

如果两个枚举用例“匹配”,而不管它们的关联值是什么,我通常要做的比较是:

我有一个协议:

protocol Matchable {
static func ~= (lhs: Self, rhs: Self) -> Bool
}

然后我让枚举符合它:

extension CardRank: Matchable {
static func ~= (lhs: Self, rhs: Self) -> Bool {
switch (lhs, rhs) {
case
(.number, .number),
(.jack, .jack),
(.queen, .queen),
(.king, .king),
(.ace, .ace):
return true
        

default:
return false
}
}
}


let card1: CardRank = .number(1)
let card2: CardRank = .number(2)
let card3: CardRank = .jack


print(card1 ~= card2) // true
print(card1 ~= card3) // false

在 Swift 5.3中,你可以使用 可比较枚举特性:

enum CardRank: Comparable {
case Number(Int)
case Jack
case Queen
case King
case Ace
}


let cards: [CardRank] = [
.Queen, .Number(8), .Ace, .Number(3), .King
]


print(cards.sorted())
// [.Number(3), .Number(8), .Queen, .King, .Ace]
extension CardRank {
func isSameCaseAs(_ other: CardRank) -> Bool {
switch (self, other) {
case (.Number, .Number),
(.Jack, .Jack),
(.Queen, .Queen),
(.King, .King),
(.Ace, .Ace):
return true
default:
return false
}
}
}


let number = CardRank.Number(1)
let otherNumber = CardRank.Number(2)
number.isSameCaseAs(otherNumber) // true

只需创建一个扩展并忽略相关联的类型。