Swift枚举

目录

引入

枚举定义了一组相关值的通用类型, 使您可以在代码中以类型安全的方式处理这些值。

Swift中的枚举要灵活得多, 不必为枚举的每种情况都提供一个值。 如果为每个枚举情形提供一个值 (称为原始值),该值可以是字符串、字符或任何整数或浮点类型的值。

另外, 枚举例可以指定任何类型的关联值, 与每个不同的例值一起存储, 这与其他语言中的联合或变体非常相似。 可以将一组相关的通用情况定义为一个枚举的一部分, 每个枚举都有一组与之相关的适当类型的不同值。

Swift中的枚举本身就是一流的类型。 它们采用了许多传统上只有类才支持的功能。

枚举语法

可以使用 enum 关键字引入枚举, 定义放在花括号中。

enum SomeEnumeration {
    // enumeration definition goes here
}

示例

enum CompassPoint {
    case north
    case south
    case east
    case west
}

同一行多个case, 使用逗号分隔。

enum Planet {
    case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}

赋值给变量, 值的类型会被推断出来。

var directionToHead = CompassPoint.west

// 类型确定以后, 后续更新可以使用更短的点语法
directionToHead = .east

使用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"

在考虑枚举的情况时, 开关语句必须详尽无遗。 如果省略了 .west 的情况, 这段代码将无法编译, 因为它没有考虑 CompassPoint 情况的完整列表。 要求穷举可确保不会意外遗漏枚举的情况。

如果无法考虑到所有情况, 可以提供一种默认处理。

let somePlanet = Planet.earth
switch somePlanet {
case .earth:
    print("Mostly harmless")
default:
    print("Not a safe place for humans")
}
// Prints "Mostly harmless"

遍历枚举值

对于某些枚举来说, 收集该枚举的所有Case是非常有用的。 可以在枚举名称后编写: CaseIterable 来实现这一点。 Swift将所有情况的集合公开为枚举类型的 allCases 属性。

enum Beverage: CaseIterable {
    case coffee, tea, juice
}
let numberOfChoices = Beverage.allCases.count
print("\(numberOfChoices) beverages available")
// Prints "3 beverages available"

for beverage in Beverage.allCases {
    print(beverage)
}
// coffee
// tea
// juice

关联值

前面展示了枚举的情况本身是如何定义(和键入)值的。 你可以将一个常量或变量设置为 Planet.earth, 然后检查该值。 有时在存储例值的同时存储其他类型的值也很有用。 这些附加信息被称为关联值, 每次在代码中使用该大小写作为值时, 关联值都会有所不同。

你可以定义Swift枚举来存储任何给定类型的关联值, 如果需要, 枚举的每种情况的值类型都可以不同。 类似的枚举在其他编程语言中被称为区分联合、 标记联合或变体。

enum Barcode {
    case upc(Int, Int, Int, Int)
    case qrCode(String)
}

var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")

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."

如果枚举关联值都是作为常量或者变量提取, 可以简洁化为一个注释。

switch productBarcode {
case let .upc(numberSystem, manufacturer, product, check):
    print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).")
case let .qrCode(productCode):
    print("QR code: \(productCode).")
}
// Prints "QR code: ABCDEFGHIJKLMNOP."

原始值

关联值中的条形码示例展示了枚举实例如何声明它们存储不同类型的关联值。 作为关联值的替代方法, 枚举示例可以预先填充默认值(称为原始值),这些值都是相同类型的。

enum ASCIIControlCharacter: Character {
    case tab = "\t"
    case lineFeed = "\n"
    case carriageReturn = "\r"
}

原始值可以是字符串、 字符或任何整数或浮点数类型。 每个原始值在其枚举声明中必须是唯一的。

隐式分配原始值

在处理存储整数或字符串原始值的枚举时, 不必为每种情况显式分配原始值。 如果不这样做, Swift会自动为您赋值。

例如当原始值使用整数时, 每种情况的隐式值都比前一种情况多一个。 如果第一种情况没有设置值, 则其值为0。

enum Planet: Int {
    // Planet.mercury 的显式原始值为 1,Planet.venus 的隐式原始值为 2,以此类推。
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}

// 当使用字符串作为原始值时,每个情况的隐含值就是该情况名称的文本。
enum CompassPoint: String {
    case north, south, east, west
}

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

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"

递归枚举

递归枚举是一种枚举, 在一个或多个枚举情况下, 枚举的另一个实例是相关值。 你可以在枚举实例前写入 indirect, 告诉编译器插入必要的间接层, 从而表明该枚举实例是递归的。

enum ArithmeticExpression {
    case number(Int)
    indirect case addition(ArithmeticExpression, ArithmeticExpression)
    indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}

// 如果所有值都是间接的,可以在枚举前面写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"

该函数通过简单地返回相关值来求值一个普通数字。 它通过求左边的表达式、 求右边的表达式, 然后进行加法或乘法运算。