# 枚举

**枚举为一组相关的值定义了一个共同的类型**

在`C`语言中，枚举只能为一组整型值分配相关联的名称。

Swift 中的枚举更加灵活，不必给每一个枚举成员提供一个值。如果给枚举成员提供一个值（称为**原始值**），则该值的类型可以是字符串、字符，或是一个整型值或浮点数。

枚举成员可以指定任意类型的**关联值**存储到枚举成员中。你可以在一个枚举中定义一组相关的枚举成员，每一个枚举成员都可以有适当类型的关联值。

在Swift中，枚举是一种值类型。支持属性，方法，构造函数，还可以实现协议。

## 1. 定义枚举

使用`enum`关键字定义枚举

```swift

// 定义枚举可以不绑定任何值
enum CompassPoint {
    case north
    case south
    case east
    case west
}

// 可以定义在一行，使用逗号分隔
enum Planet {
    case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}

// 使用enum

var directionToHead = CompassPoint.west      // 可以类型推断

var directionToHead : CompassPoint = .west   // 指定类型解释，可以省略枚举名

```

> 与`C`和 `Objective-C` 不同，`Swift` 的枚举成员在被创建时不会被赋予一个默认的整型值。在上面的 CompassPoint 例子中，north，south，east 和 west 不会被隐式地赋值为 0，1，2 和 3。相反，这些枚举成员本身就是完备的值，这些值的类型是已经明确定义好的 CompassPoint 类型。

## 2. 遍历枚举

枚举实现`CaseIterable`协议，协议提供了`allCases`属性，返回所有的枚举值

```swift

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


for tmpcase in Beverage.allCases {
    print(tmpcase)
}

```

## 3. 关联值

你可以定义 `Swift` 枚举来存储任意类型的关联值，如果需要的话，每个枚举成员的关联值类型可以各不相同。枚举的这种特性跟其他语言中的`可识别联合（discriminated unions）`，`标签联合（tagged unions）`，或者`变体（variants）`相似。

如下定义了枚举`HttpResponse`,

枚举值`success`绑定`Int`和`Data`型的数据，代表状态码和返回的数据

枚举值`failed`绑定`Int`和`Error`型的数据，代表状态码和错误信息

```swift

enum HttpResponse{
    case success(Int,Data)
    case fail(Int,Error)
}


// 
var respoonse = HttpResponse.success(200,Data())

response = .fail(400,Error())

```

## 4. 原始值

Swift 可以为每个枚举值设置一个固定的默认值，称为原始值。

原始值必须类型相同，可以为**字符串**、**字符**，或是一个**整型值**或**浮点数**。

```swift

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

// 访问原始值
let c = ASCIIControlCharacter.tab.raw

```

### 4.1 原始值的隐式赋值

* 整型原始值隐式赋值

```swift

// 指定第一个枚举值的原始值，其他原始值以递增1 的方式，隐式赋值；第一个原始值为指定，默认为0
enum Planet: Int {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
```

* 字符串原始值隐式赋值

```swift
// 每个枚举成员的隐式原始值为该枚举成员的名称。
enum CompassPoint: String {
    case north, south, east, west
}
```

### 4.2 以原始值初始化枚举实例

如果在定义枚举类型的时候使用了原始值，那么将会自动获得一个初始化方法，这个方法接收一个叫做 `rawValue` 的参数。

参数类型即为原始值类型，**返回值则是枚举成员或 nil。**

```swift

//  返回值则是枚举成员或 nil
let possiblePlanet = Planet(rawValue: 7)

// 
```

### 4.3 原始值特性

> 枚举的原始值必须指定类型，同一个枚举类型的不同枚举值都是相同的类型

> 枚举值的原始值在定义时指定，且不可以修改；如果整型或字符串原始值未指定，一般会隐式的赋值

> 原始值构造器是一个可失败构造器，因为并不是每一个原始值都有与之对应的枚举成员。

## 5. 原始值和关联值的区别

* 原始值是在定义枚举时被预先填充的值。对于一个特定的枚举成员，它的原始值始终不变。
* 关联值是创建一个基于枚举成员的常量或变量时才设置的值，枚举成员的关联值可以变化。

## 6. 递归枚举

**递归枚举**是一种枚举类型，它有一个或多个枚举成员**使用该枚举类型的实例作为关联值**。

在`enum`前使用`indirect`指定枚举类型中一个或多个枚举成员使用该枚举类型的实例作为关联值。

在`case`前使用`indirect`指定当前枚举值使用枚举类型的实例作为关联值

```swift

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

```

## 7. C/C++/Swift枚举对比

C/C++中的枚举 ：

* 是为一组名字绑定整型值（且只能绑定整型值，不能绑定浮点值，字符串或者其他类型）
* 本质上每个枚举值还是整型，是简单的基本算术类型

Swift的枚举：

* 是一种类类型，可以有属性，方法以及构造器，简而言之具有封装性
* (原始值) 可以为每个枚举值绑定一个固定的值（这个值可以是整型，浮点型和字符串）
* (关联值) 可以为每个枚举值绑定一组数据(每个枚举值的数据类型可以不一样)
