英文原文
现在你有这样的一个枚举:
enum Expression{
case number(Double)
case string(String)
}
你想要对它进行相等性的判断,因为这个枚举有关联值,相等性的判断必须手动的来进行添加。所以你声明了==函数
extension Expression: Equatable{
static func ==(lhs: Expression, rhs: Expression) -> Bool{
switch (lhs, rhs) {
case let (.number(l), .number(r)):
return l == r
case let (.string(l), .string(r)):
return l == r
default:
return false
}
}
}
当这两个参数拥有相同的case的时候这两个case就会被得到处理,除此之外,添加了一个默认模式(default)来返回false。这样来使用时简单、简短和正确的:
Expression.number(1) == .number(1)
Expression.number(1) == .string("a")
Defult case 的使用将使得穷尽的检查失去效果
然而,你的实现由一个严重的瑕疵,如果你再添加一个case到你的枚举当中去,编译器将不会提醒你,你的==实现现在是不完整的。让我们在枚举中添加第三个case:
enum Expression {
case number(Double)
case string(String)
case bool(Bool)
}
对于编译器而言,这是完全正确的。在下面的执行中,你的代码将返回错误的结果:
Expression.bool(true) == .bool(true)
switch语句中的默认子句使编译器的详尽性检查无效。出于这样的原因,通常的建议就是在switch语句中应该尽量避免使用default。
如果可以避免,不要在switch中使用default
二次方爆炸模型
当然没有default case的缺点是有更多的样板需要去写。在这里有一个==的版本穷尽了三个所有的case:
extension Expression: Equatable{
static func ==(lhs: Expression, rhs: Expression) -> Bool{
switch (lhs, rhs) {
case let (.number(l), .number(r)): return l == r
case let (.string(l), .string(r)): return l == r
case let (.bool(l), .bool(r)): return l == r
case (.number, .string),
(.number, .bool),
(.string, .number),
(.string, .bool),
(.bool, .number),
(.bool, .string): return false
}
}
}
啊,这样写一点也不好玩,当有更多的case的时候,这将变得很糟糕,switch语句必须区分的状态数量与枚举中的case数量二次方增长。
从二次方到线性增长
您可以通过_占位符模式这样的一些更加聪明的方式来使其更加易于管理。虽然我们在上面看到一个默认子句是不够的,但是每种情况都是一个模式。 switch语句中的最后六个模式变成三个:
extension Expression: Equatable{
static func ==(lhs: Expression, rhs: Expression) -> Bool{
switch (lhs, rhs) {
case let (.number(l), number(r)): return l == r
case let (.bool(l), .bool(r)): return l == r
case let (.string(l), .string(r)): return l == r
case (.number, _),
(.string, _),
(.bool, _): return false
}
}
}
通过这种方式来做更好的是,枚举中的每个附加情况只会向switch语句增加两条线 - 它不再二次扩展。并且您可以从编译器的穷举检查中获益:添加新的case会在==中引发错误。