Enums, Equatable, and exhaustive

2017-08-17  本文已影响0人  TCJing

英文原文
现在你有这样的一个枚举:

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会在==中引发错误。

上一篇下一篇

猜你喜欢

热点阅读