枚举定义一组相关值的公共类型,并允许您在代码中以类型安全的方式使用这些值。
如果你熟悉C,你会知道C枚举分配相关的名称到一组整数值。 Swift中的枚举更灵活,并且不必为枚举的每种情况提供一个值。 如果为每个枚举类型提供一个值(称为“原始”值),则该值可以是字符串,字符或任何整数或浮点类型的值。
枚举语法
enum SomeEnumeration {
// enumeration definition goes here
}
这里有一个指南针的枚举示例
enum CompassPoint {
case north
case south
case east
case west
}
枚举中定义的值(如北,南,东和西)是其枚举的例子。 您可以使用case关键字来引入新的枚举案例。
注意:
与C和Objective-C不同,Swift枚举案例在创建时未分配默认整数值。 在上面的CompassPoint例子中,北,南,东和西不是隐含地等于0,1,2和3.相反,不同的枚举情况是它们自己正确的完全成熟的值,具有明确定义的CompassPoint类型。
多个案例可以出现在单个行上,以逗号分隔:
enum Planet {
case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}
当使用CompassPoint的某个可能值初始化时,将推断directionToHead的类型。 一旦directionToHead声明为CompassPoint,您可以使用较短的点语法将其设置为不同的CompassPoint值:
var directionToHead = CompassPoint.west
directionToHead = .east
directionToHead的类型是已知的,因此可以在设置其值时删除类型。 这使得在使用显式类型枚举值时具有高度可读性的代码。
使用Switch语句匹配枚举值
您可以使用switch语句匹配各个枚举值:
directionToHead = .south
switch directionToHead {
case .north:
print("Lots of planets have a north")
case .south:
print("Watch out for penguins")
case .east:
print("Where the sun rises")
case .west:
print("Where the skies are blue")
}
// Prints "Watch out for penguins"
如控制流中所述,在考虑枚举的情况下,switch语句必须是穷举的。 如果省略了.west的情况,此代码不会编译,因为它不会考虑CompassPoint案例的完整列表。 需要穷举确保枚举案例不会被意外地省略。
当不想为每个枚举案例提供案例时,可以提供一个默认案例,以涵盖未明确处理的任何案例:
let somePlanet = Planet.earth
switch somePlanet {
case .earth:
print("Mostly harmless")
default:
print("Not a safe place for humans")
}
// Prints "Mostly harmless"
相关值
在Swift中,定义任一类型的产品条形码的枚举可能如下所示:
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
这个定义不提供任何实际的Int或String值 - 它只是定义了当Barcode常量和变量等于Barcode.upc或Barcode.qrCode时,它们可以存储的关联值的类型。
然后可以使用以下任一类型创建新条形码:
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
此示例创建一个名为productBarcode的新变量,并为其分配值为Barcode.upc的关联元组值(8,85909,51226,3)。
可以为同一产品分配不同类型的条形码:
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
此时,原始的Barcode.upc及其整数值将被新的Barcode.qrCode及其字符串值替换。 Barcode类型的常量和变量可以存储.upc或.qrCode(及其关联的值),但它们只能在任何给定时间存储它们之一。
如前所述,可以使用switch语句检查不同的条形码类型。 然而,这次,相关的值可以作为switch语句的一部分提取。 您可以将每个关联值提取为常量(使用let前缀)或变量(使用var前缀),以在交换机主体中使用:
switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
print("QR code: \(productCode).")
}
// Prints "QR code: ABCDEFGHIJKLMNOP."
原始值
关联值中的条形码示例显示枚举的案例如何声明它们存储不同类型的关联值。 作为关联值的替代,枚举情况可以预填充有默认值(称为原始值),它们都是相同类型。
这里有一个示例,存储原始ASCII值和命名枚举案例:
enum ASCIIControlCharacter: Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}
原始值可以是字符串,字符或任何整数或浮点数类型。 每个原始值在其枚举声明中必须是唯一的。
隐式分配的原始值
当您使用存储整数或字符串原始值的枚举时,不必为每种情况显式分配原始值。 当你不这样做时,Swift会自动为你分配值。
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
在上面的示例中,Planet.mercury具有明确的原始值1,Planet.venus具有隐含原始值2,以此类推。
当字符串用于原始值时,每个案例的隐式值是该案例名称的文本。
下面的枚举是CompassPoint枚举的细化,用字符串原始值表示每个方向的名称:
enum CompassPoint: String {
case north, south, east, west
}
在上面的示例中,CompassPoint.south具有“south”的隐式原始值,以此类推。
您可以使用其rawValue属性访问枚举案例的原始值:
let earthsOrder = Planet.earth.rawValue
// earthsOrder is 3
let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection is "west"
从原始值初始化
如果使用原始值类型定义枚举,枚举将自动接收一个初始值,该值将原始值的类型(作为名为rawValue的参数)赋值,并返回枚举大小或nil。 您可以使用此初始化程序尝试创建枚举的新实例。
let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet is of type Planet? and equals Planet.uranus
不是所有可能的Int值都会找到匹配的行星。 因此,原始值初始值始终返回一个可选的枚举大小。 在上面的示例中,possiblePlanet类型为Planet?或“可选Planet”。
如果您尝试找到一个位置为11的Planet,则由原始值初始值器返回的可选Planet值将为nil:
let positionToFind = 11
if let somePlanet = Planet(rawValue: positionToFind) {
switch somePlanet {
case .earth:
print("Mostly harmless")
default:
print("Not a safe place for humans")
}
} else {
print("There isn't a planet at position \(positionToFind)")
}
// Prints "There isn't a planet at position 11"
此示例使用可选绑定尝试访问原始值为11的Planet。语句if let somePlanet = Planet(rawValue:11)创建一个可选Planet,并将somePlanet设置为可选Planet的值,如果可以检索 。
在这种情况下,不可能检索位置为11的Planet,因此执行else分支。
递归枚举
递归枚举是枚举的另一个枚举实例作为一个或多个枚举例的关联值。 您通过在其前面写入indirect表示枚举案例是递归的,它告诉编译器插入必要的间接层。
例如,这里是存储简单算术表达式的枚举:
enum ArithmeticExpression {
case number(Int)
indirect case addition(ArithmeticExpression, ArithmeticExpression)
indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}
您还可以在枚举开始之前写入indirect,以便为需要它的所有枚举的情况启用indirect:
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
}
let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
递归函数是处理具有递归结构的数据的简单方法。 例如,下面是一个计算算术表达式的函数:
func evaluate(_ expression: ArithmeticExpression) -> Int {
switch expression {
case let .number(value):
return value
case let .addition(left, right):
return evaluate(left) + evaluate(right)
case let .multiplication(left, right):
return evaluate(left) * evaluate(right)
}
}
print(evaluate(product))
// Prints "18"
此函数通过简单地返回相关值来计算一个简单数字。 它通过评估左侧的表达式,评估右侧的表达式,然后将它们相加或相乘来评估加法或乘法。