阅读完如何测试 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.")
}
但是,这无法编译:
let number = CardRank.Number(5)
if number == CardRank.Number {
print("You must play a face card!")
}
...并给出以下错误消息:
二元运算符“==”不能应用于“CardRank”和“(Int) -> CardRank”类型的操作数
我假设这是因为它需要一个完整的类型,并且
CardRank.Number
没有指定整个类型,而 CardRank.Number(2)
则指定了。然而,在这种情况下,我希望它匹配any数字;不只是特定的一个。
显然我可以使用 switch 语句,但实现
==
运算符的全部目的是避免这种冗长的解决方案:
switch number {
case .Number:
print("You must play a face card!")
default:
break
}
有没有办法将枚举与关联值进行比较,同时忽略其关联值?
注意:我意识到我可以将
==
方法中的大小写更改为 case (.Number, .Number): return true
,但是,尽管它会正确返回 true,但我的比较仍然看起来像是与特定数字进行比较(number == CardRank.Number(2)
;其中 2 是虚拟值)而不是任何数字 (number == CardRank.Number
)。
编辑: 正如 Etan 指出的,您可以省略
(_)
通配符匹配以更干净地使用它:
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
计算属性。
不幸的是,在 Swift 1.x 中没有其他方法,所以你必须使用
switch
,它不像 Swift 2 的版本那么优雅,你可以使用 if case
:
if case .Number = number {
//ignore the value
}
if case .Number(let x) = number {
//without ignoring
}
在 Swift 4.2 中,如果所有关联值都符合
Equatable
,则将合成 Equatable
。您所需要做的就是添加 Equatable
。
enum CardRank: Equatable {
case Number(Int)
case Jack
case Queen
case King
case Ace
}
https://developer.apple.com/documentation/swift/equatable?changes=_3
我通常会比较两个枚举案例是否“匹配”,无论它们的关联值如何:
我有一个协议
Matchable
:
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
这里有一个更简单的方法:
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!")
}
我不想遵守
Equatable
(这对我也没有帮助),并且我想过滤除特定案例之外的其他情况,因此我必须编写以下内容,而不是简单地写card != .Number
。 (我针对这个问题调整了代码。)
enum CardRank {
...
var isNumber: Bool {
if case .Number = self { return true }
return false
}
}
所以我可以在复杂的情况下写不是数字:
if something && !card.isNumber { ... }
我希望我可以只写
card != .Number
,但编译器总是抱怨表达式类型不明确,没有更多上下文。也许在即将推出的 swift 版本中!
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
只需创建一个扩展并忽略关联的类型。
您不需要
func ==
或 Equatable
。只需使用枚举案例模式。
let rank = CardRank.Ace
if case .Ace = rank { print("Snoopy") }
从 Swift 5.3 开始,您可以使用 Comparable Enums 功能:
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]
let cardRank = CardRank.Number(10)
guard let case .Number(someNumber) = cardRank else {
return
}
print(someNum)