枚举
概述
枚举 (Enumeration) 为定义一组相关值提供了一种强大的方式,使代码更易读、更易维护。Swift 中的枚举比许多其他语言中的枚举更加灵活。
1enum 枚举名称 {
2 case 案例1
3 case 案例2
4 // ...
5}
基本语法
定义枚举
1enum CompassPoint {
2 case north
3 case south
4 case east
5 case west
6}
简写形式
多个 case 可以写在同一行,用逗号分隔:
1enum CompassPoint {
2 case north, south, east, west
3}
创建和使用枚举
1let direction = CompassPoint.north
2
3// 使用 switch 匹配
4switch direction {
5case .north:
6 print("向北")
7case .south:
8 print("向南")
9case .east:
10 print("向东")
11case .west:
12 print("向西")
13}
原始值 (Raw Values)
字符串原始值
1enum Planet: String {
2 case mercury = "Mercury"
3 case venus = "Venus"
4 case earth = "Earth"
5 case mars = "Mars"
6}
7
8let earth = Planet.earth
9print(earth.rawValue) // 输出: "Earth"
整数原始值
1enum StatusCode: Int {
2 case success = 200
3 case notFound = 404
4 case serverError = 500
5}
6
7let status = StatusCode.success
8print(status.rawValue) // 输出: 200
自动赋值
对于整数类型,如果没有指定值,Swift 会自动递增赋值:
1enum Planet: Int {
2 case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
3}
4
5print(Planet.venus.rawValue) // 输出: 2
6print(Planet.earth.rawValue) // 输出: 3
对于字符串类型,Swift 会自动使用 case 名称作为原始值:
1enum Direction: String {
2 case north, south, east, west
3}
4
5print(Direction.north.rawValue) // 输出: "north"
从原始值创建枚举
1if let success = StatusCode(rawValue: 200) {
2 print("成功: \(success)")
3}
4
5if let notFound = StatusCode(rawValue: 404) {
6 print("未找到")
7}
关联值 (Associated Values)
关联值允许枚举的每个 case 携带额外的数据:
1enum Barcode {
2 case upc(Int, Int, Int, Int)
3 case qrCode(String)
4}
5
6let productCode = Barcode.qrCode("ABC123456")
7
8switch productCode {
9case .upc(let numberSystem, let manufacturer, let product, let check):
10 print("UPC: \(numberSystem)-\(manufacturer)-\(product)-\(check)")
11case .qrCode(let code):
12 print("QR码: \(code)")
13}
14// 输出: QR码: ABC123456
简写形式
如果所有关联值都是常量或变量,可以用 let 或 var 统一声明:
1switch productCode {
2case let .upc(numberSystem, manufacturer, product, check):
3 print("UPC: \(numberSystem)-\(manufacturer)-\(product)-\(check)")
4case let .qrCode(code):
5 print("QR码: \(code)")
6}
switch 匹配与 if 判断
switch 基本用法
switch 语句用于匹配枚举的所有 case,必须覆盖所有情况或包含 default 分支:
1enum Weather {
2 case sunny(temperature: Double)
3 case rainy(intensity: Double)
4 case snowy(accumulation: Double)
5}
6
7let currentWeather = Weather.sunny(temperature: 28.5)
8
9switch currentWeather {
10case .sunny(let temp):
11 print("晴天,温度 \(temp)°C")
12case .rainy(let intensity):
13 print("雨天,强度 \(intensity)")
14case .snowy(let accumulation):
15 print("雪天,积雪 \(accumulation)cm")
16}
17// 输出: 晴天,温度 28.5°C
where 子句
使用 where 子句添加额外的条件判断:
1switch currentWeather {
2case .sunny(let temp) where temp > 30:
3 print("高温天气,注意防暑")
4case .sunny(let temp) where temp < 10:
5 print("寒冷天气,注意保暖")
6case .sunny(let temp):
7 print("晴天,温度 \(temp)°C")
8case .rainy(let intensity):
9 print("雨天,强度 \(intensity)")
10case .snowy(let accumulation):
11 print("雪天,积雪 \(accumulation)cm")
12}
隐式穷举
当 switch 语句覆盖枚举的所有 case 时,不需要 default 分支:
1enum Direction {
2 case north, south, east, west
3}
4
5let dir: Direction = .north
6
7switch dir {
8case .north: print("北")
9case .south: print("南")
10case .east: print("东")
11case .west: print("西")
12}
if case 模式匹配
使用 if case 进行单分支模式匹配,比 switch 更简洁:
1enum Status {
2 case success(String)
3 case failure(Int)
4}
5
6let result: Status = .success("操作成功")
7
8// 匹配单个 case
9if case .success(let message) = result {
10 print("成功: \(message)")
11}
12// 输出: 成功: 操作成功
13
14// 不匹配时不执行
15if case .failure(let code) = result {
16 print("失败: \(code)")
17}
18// 不打印任何内容
guard case 模式匹配
使用 guard case 在条件不匹配时提前退出:
1func processResult(_ result: Status) {
2 guard case .success(let message) = result else {
3 print("处理失败")
4 return
5 }
6 print("处理成功: \(message)")
7}
8
9processResult(.success("完成")) // 输出: 处理成功: 完成
10processResult(.failure(404)) // 输出: 处理失败
组合条件
if case 可以与其他条件组合:
1let results: [Status] = [
2 .success("成功1"),
3 .failure(404),
4 .success("成功2"),
5 .failure(500)
6]
7
8for result in results {
9 if case .failure(let code) = result, code >= 500 {
10 print("服务器错误: \(code)")
11 }
12}
13// 输出: 服务器错误: 500
多 case 匹配
使用逗号分隔多个 case:
1enum Shape {
2 case circle(radius: Double)
3 case rectangle(width: Double, height: Double)
4 case triangle(base: Double, height: Double)
5}
6
7let shape: Shape = .circle(radius: 5)
8
9switch shape {
10case .circle, .triangle:
11 print("曲线或三角形")
12case .rectangle:
13 print("矩形")
14}
提取多个关联值
1enum Coordinate {
2 case point(x: Double, y: Double)
3 case line(start: (Double, Double), end: (Double, Double))
4}
5
6let coord: Coordinate = .line(start: (0, 0), end: (10, 10))
7
8switch coord {
9case .point(let x, let y):
10 print("点: (\(x), \(y))")
11case .line(let (x1, y1), let (x2, y2)):
12 print("线段: (\(x1),\(y1)) -> (\(x2),\(y2))")
13}
解包可选枚举
使用 if case 解包可选值:
1enum Optional<T> {
2 case some(T)
3 case none
4}
5
6let value: Optional<Int> = .some(42)
7
8if case .some(let wrapped) = value {
9 print("有值: \(wrapped)")
10}
11// 输出: 有值: 42
12
13// 相当于
14if case Optional.some(let wrapped) = value {
15 print("有值: \(wrapped)")
16}
递归枚举
递归枚举是指枚举的 case 的关联值类型包含该枚举本身。使用 indirect 关键字声明:
1indirect enum ArithmeticExpression {
2 case number(Int)
3 case addition(ArithmeticExpression, ArithmeticExpression)
4 case multiplication(ArithmeticExpression, ArithmeticExpression)
5}
6
7func evaluate(_ expression: ArithmeticExpression) -> Int {
8 switch expression {
9 case .number(let value):
10 return value
11 case .addition(let left, let right):
12 return evaluate(left) + evaluate(right)
13 case .multiplication(let left, let right):
14 return evaluate(left) * evaluate(right)
15 }
16}
17
18// 创建表达式: (2 + 3) * 4
19let expression = ArithmeticExpression.multiplication(
20 .addition(.number(2), .number(3)),
21 .number(4)
22)
23
24print(evaluate(expression)) // 输出: 20
也可以在特定 case 前添加 indirect:
1enum Tree {
2 case empty
3 indirect case node(value: Int, left: Tree, right: Tree)
4}
枚举的方法和属性
实例属性
1enum Weekday: String {
2 case monday = "周一"
3 case tuesday = "周二"
4 case wednesday = "周三"
5 case thursday = "周四"
6 case friday = "周五"
7 case saturday = "周六"
8 case sunday = "周日"
9
10 var isWeekend: Bool {
11 switch self {
12 case .saturday, .sunday:
13 return true
14 default:
15 return false
16 }
17 }
18}
19
20print(Weekday.monday.isWeekend) // 输出: false
21print(Weekday.saturday.isWeekend) // 输出: true
静态方法
1enum Temperature {
2 case celsius(Double)
3 case fahrenheit(Double)
4
5 static func fromCelsius(_ value: Double) -> Temperature {
6 return .celsius(value)
7 }
8
9 static func fromFahrenheit(_ value: Double) -> Temperature {
10 return .fahrenheit(value)
11 }
12}
13
14let temp = Temperature.fromCelsius(25.0)
实例方法
1enum Counter {
2 case value(Int)
3
4 mutating func increment() {
5 if case .value(let current) = self {
6 self = .value(current + 1)
7 }
8 }
9
10 mutating func increment(by amount: Int) {
11 if case .value(let current) = self {
12 self = .value(current + amount)
13 }
14 }
15}
16
17var counter = Counter.value(0)
18counter.increment()
19counter.increment(by: 5)
CaseIterable 协议
让枚举遵循 CaseIterable 协议,可以遍历所有 case:
1enum Season: String, CaseIterable {
2 case spring = "春"
3 case summer = "夏"
4 case autumn = "秋"
5 case winter = "冬"
6}
7
8for season in Season.allCases {
9 print(season.rawValue)
10}
11// 输出: 春 夏 秋 冬
枚举初始化
原始值初始化
1enum Color: String {
2 case red = "红色"
3 case green = "绿色"
4 case blue = "蓝色"
5
6 init?(colorName: String) {
7 if let color = Color(rawValue: colorName) {
8 self = color
9 } else {
10 return nil
11 }
12 }
13}
关联值初始化
1enum Result {
2 case success(Int)
3 case failure(String)
4
5 init?(code: Int) {
6 switch code {
7 case 200...299:
8 self = .success(code)
9 default:
10 return nil
11 }
12 }
13}
协议遵循
枚举可以遵循多种协议:
1// Equatable 自动合成
2enum Status: Equatable {
3 case active
4 case inactive
5 case pending
6}
7
8// Codable
9enum Response: Codable {
10 case data(String)
11 case error(Int)
12}
13
14// 自定义协议
15protocol Describable {
16 var description: String { get }
17}
18
19enum Shape: Describable {
20 case circle(radius: Double)
21 case rectangle(width: Double, height: Double)
22
23 var description: String {
24 switch self {
25 case .circle(let r):
26 return "圆形,半径: \(r)"
27 case .rectangle(let w, let h):
28 return "矩形,宽: \(w),高: \(h)"
29 }
30 }
31}
常见用法示例
网络请求结果
1enum NetworkResult {
2 case success(data: Data)
3 case failure(error: Error)
4 case timeout(seconds: TimeInterval)
5 case offline
6}
7
8func handleResponse(_ result: NetworkResult) {
9 switch result {
10 case .success(let data):
11 print("收到数据: \(data.count) 字节")
12 case .failure(let error):
13 print("请求失败: \(error.localizedDescription)")
14 case .timeout(let seconds):
15 print("请求超时: \(seconds) 秒")
16 case .offline:
17 print("无网络连接")
18 }
19}
可选值模拟
1enum Optional<T> {
2 case some(T)
3 case none
4}
5
6let value: Optional<Int> = .some(42)
7
8switch value {
9case .some(let wrapped):
10 print("有值: \(wrapped)")
11case .none:
12 print("无值")
13}
枚举与类的区别
| № | 特性 | 枚举 | 类 |
|---|---|---|---|
| 0 | 实例创建 | 值类型 | 引用类型 |
| 1 | 继承 | 不支持 | 支持 |
| 2 | 存储属性 | 不支持 | 支持 |
| 3 | 计算属性 | 支持 | 支持 |
| 4 | 实例方法 | 支持(可修改 self) | 支持 |
| 5 | 静态属性/方法 | 支持 | 支持 |
枚举与关联值的比较
| № | 特性 | 原始值 (Raw Values) | 关联值 (Associated Values) |
|---|---|---|---|
| 0 | 值类型 | 编译时确定 | 运行时确定 |
| 1 | 类型一致性 | 所有 case 相同类型 | 每个 case 可不同类型 |
| 2 | 用途 | 表示固定映射 | 表示动态数据 |
| 3 | 获取方式 | rawValue 属性 |
switch 或 if case 提取 |