Swift 4.2:可枚举的枚举

在本文中,我希望解释如何使用新的CaseIterable协议枚举枚举中的所有案例,并解释其一些局限性。

什么是CaseIterable?

这是Swift 4.2中引入的协议。 它会按定义的顺序自动综合枚举中所有个案的集合,但是,如果枚举包含关联的值,则必须提供自己的allCases实现(稍后会详细介绍)。

让我们看一个美味的例子🍕🤤

枚举披萨:CaseIterable { 
那不勒斯案件
芝加哥案例
案例纽约
西西里案例
凯斯希腊语
加州案例
}让pizzaTypes = Pizza.allCases.map {
“ \($ 0)”
} print(pizzaTypes) // [“那不勒斯”,“芝加哥”,“纽约”,“西西里人”,“希腊”,“加利福尼亚”]

使我们的枚举符合CaseIterable 我们可以访问allCases属性,该属性会自动生成并提供Pizza枚举中所有案例的集合。 这不是很好! 我们不再需要自己维护所有案例列表的开销,而是以声明的顺序为我们提供了案例集合。 您还可以利用Collection协议随附的功能池,以便执行诸如mapfilter的功能

即使CaseIterable专门用于枚举,也可以与其他类型兼容 ,但这需要维护allCases实现。 不幸的是,这是开始出现限制的地方,因此在采用这种新颖而令人兴奋的协议之前,让我们先看看这些限制。

局限性

第一个限制是自动合成。 由于编译器会自动合成符合CaseIterable协议的一致性,因此只能在定义了类型的同一文件中声明一致性,即在类型定义本身上或在与扩展名相同的文件中声明一致性。 让我们看一些插图。

这些是允许的🤩:

  // Pizza.swift 
枚举披萨:CaseIterable {
....
}
**** 要么 ****
枚举披萨{
....
}扩展披萨:CaseIterable {}

但这不是😫:

  // Pizza.swift 
枚举披萨{
....
}
// SomeOtherFile.swiftextension披萨:CaseIterable {}

关联值是另一个限制,自动综合不适用于具有关联值的枚举。 即使关联值符合CaseIterable也是如此 。 这是有道理的,因为将关联的值添加到枚举将使所有情况的可能性变得无限,当然,您可以提供自己的实现,但这会违背该协议的目的。

让我们看一个以上问题的例子。

 枚举披萨{ 
案件那不勒斯(NeapolitanTypes)
芝加哥案例
}枚举NeapolitanTypes {
Case marinara
玛格丽特凯斯
}扩展比萨:CaseIterable {
静态var allCases:[披萨] {
返回[.neapolitan(.marinara),
.neapolitan(.margherita),
。芝加哥]
}
}

如您所见,我正在提供allCases属性的自定义实现,尽管这看起来似乎很容易编写,但由于其维护性质,可能导致许多问题。 例如,如果将一个新案例添加到Pizza枚举,则allCases的调用者可能需要更改,并且编译器不会提醒我们allCases实现不再有效。 这可能会导致难以调试的重大更改。 解决此问题的一种方法是通过详尽的开关来识别Pizza枚举的所有情况,这会在编译时产生错误。 但是,这不是一个完美的解决方案,因为您必须记住要修复自定义allCases实现以及编译器错误。

结论

在我看来, CaseIterable是一个有用的协议,它提供了有用的功能,您可以将其附加到枚举上。 但是,在接受之前有一些限制要考虑,因为我们前面已经看到,这些限制很容易克服,但维护成本很小。

试用CaseIterable协议 并让我们知道它的一些潜在应用(我去吃掉Pizza.neapolitan(.marinara) 😋)。