11.3 枚举
在C和Objective-C中,枚举用来管理一组相关常量集合,通过使用枚举可以提高程序的可读性,使代码更清晰,更易于维护。而在Swift中,枚举的作用已经不仅仅是定义一组常量、提高程序的可读性了,它还具有了面向对象特性。
我们先来看Swift声明。Swift中也是使用enum关键词声明枚举类型,具体定义放在一对大括号内,枚举的语法格式如下:
enum 枚举名{枚举的定义}
“枚举名”是该枚举类型的名称。它首先应该是有效的标识符,其次应该遵守面向对象的命名规范。它应该是一个名称,如果采用英文单词命名,首字母应该大写,尽量用一个英文单词。这个命名规范也适用于类和结构体的命名。“枚举的定义”是枚举的核心,它由一组成员值和一组相关值组成。
11.3.1 成员值
在枚举类型中定义一组成员,与C和Objective-C中枚举的主要作用是一样的,不同的是,在C和Objective-C中成员值是整数类型,因此在C和Objective-C中枚举类型就是整数类型。
而在Swift中,枚举的成员值默认情况下不是整数类型,以下代码是声明枚举示例:
enum WeekDays {case Mondaycase Tuesdaycase Wednesdaycase Thursdaycase Friday}
上述代码声明了WeekDays枚举,表示一周中的每个工作日,其中定义了5个成员值:Monday、Tuesday、Wednesday、Thursday和Friday,这些成员值并不是整数类型。
在这些成员值前面还要加上case关键字,也可以将多个成员值放在同一行,用逗号隔开,如下所示:
enum WeekDays {case Monday, Tuesday, Wednesday, Thursday, Friday}
下面我们看一个示例,代码如下:
var day = WeekDays.Friday ①day = WeekDays.Wednesday ②day = .Monday ③func writeGreeting(day : WeekDays) { ④switch day { ⑤case .Monday:println("星期一好!")case .Tuesday :println("星期二好!")case .Wednesday :println("星期三好!")case .Thursday :println("星期四好!")case .Friday :println("星期五好!")} ⑥}writeGreeting(day) ⑦writeGreeting(WeekDays.Friday) ⑧
上述代码是使用WeekDays枚举的一个示例,其中第①行代码是把WeekDays枚举的成员值Friday赋值给变量day,第②行和第③行代码也是给变量day赋值,我们可以采用完整的“枚举类型名.成员值”的形式,也可以省略枚举类型采用“.成员值”的形式(见代码第③行)。这种省略形式能够访问的前提是,Swift能够根据上下文环境推断类型。因为我们已经在第①行和第②行给day变量赋值,所以即使第③行代码采用缩写,Swift也能够推断出数据类型是WeekDays。
为了方便反复调用,我们在第④行定义了writeGreeting函数。第⑤~⑥行代码使用了switch语句,枚举类型与switch语句能够很好地配合使用。在switch语句中使用枚举类型可以没有default分支,这在使用其他类型时是不允许的。使用default分支的代码如下:
func writeGreeting(day : WeekDays) {switch day {case .Monday:println("星期一好!")case .Tuesday :println("星期二好!")case .Wednesday :println("星期三好!")case .Thursday :println("星期四好!")default:println("星期五好!")}}
需要注意,在switch中使用枚举类型时,switch语句中的case必须全面包含枚举中的所有成员,不能多也不能少,包括使用default的情况下,default也表示某个枚举成员。在上面的示例中,default表示的是Friday枚举成员,在这种情况下,Friday枚举成员的case分支不能再出现了。
上述代码第⑦行和第⑧行是调用函数writeGreeting,传递的参数可以是WeekDays变量(见代码第⑦行),也可以是WeekDays中的成员值(见代码第⑧行)。
11.3.2 原始值
出于业务上的需要,要为每个成员提供某种具体类型的默认值,我们可以为枚举类型提供原始值(raw values)声明,这些原始值类型可以是:字符、字符串、整数和浮点数等。
原始值枚举的语法格式如下:
enum 枚举名 : 数据类型{case 成员名 = 默认值......}
在“枚举名”后面跟“:”和“数据类型”就可以声明原始值枚举的类型,然后在定义case成员的时候需要提供默认值。
以下代码是声明枚举示例:
enum WeekDays : Int {case Monday = 0case Tuesday = 1case Wednesday = 2case Thursday = 3case Friday = 4}
我们声明的WeekDays枚举类型的原始值类型是Int,需要给每个成员赋值,只要是Int类型都可以,但是每个分支不能重复。我们还可以采用如下简便写法,只需要给第一个成员赋值即可,后面的成员值会依次加1。
enum WeekDays : Int {case Monday = 0, Tuesday, Wednesday, Thursday, Friday}
以下是完整的示例代码:
var day = WeekDays.Fridayfunc writeGreeting(day : WeekDays) {switch day {case .Monday:println("星期一好!")case .Tuesday :println("星期二好!")case .Wednesday :println("星期三好!")case .Thursday :println("星期四好!")case .Friday :println("星期五好!")}}let friday = WeekDays.Friday.toRaw() ①let thursday = WeekDays.fromRaw(3) ②if (WeekDays.Friday.toRaw() == 4) { ③println("今天是星期五")}writeGreeting(day)writeGreeting(WeekDays.Friday)
上述代码与上一节的示例非常类似,相同的地方将不再赘述,我们重点看有标号的代码部分。其中第①行代码是通过WeekDays.Friday的方法toRaw()转换为原始值。虽然在定义的时候Friday被赋值为4,但是并不等于WeekDays.Friday就是整数4了,而是它的原始值为整数4,因此下面的比较是错误的。
if (WeekDays.Friday == 4) {println("今天是星期五")}
我们需要使用WeekDays.Friday的原始值进行比较,见代码第③行。
toRaw()方法是将成员值转换为原始值,相反fromRaw()方法是将原始值转换为成员值,见代码第②行。
11.3.3 相关值
在Swift中除了可以定义一组成员值,还可以定义一组相关值(associated values),它有点类似于C中的联合类型。下面看一个枚举类型的声明:
enum Figure {case Rectangle(Int, Int)case Circle(Int)}
枚举类型Figure(图形)有两个相关值: Rectangle(矩形)和Circle(圆形)。Rectangle和Circle是与Figure有关联的相关值,它们都是元组类型,对于一个特定的Figure实例,只能是其中一个相关值。从这一点来看,枚举类型的相关值类似于C中的联合类型。
下面我们看一个示例,代码如下:
func printFigure(figure : Figure) { ①switch figure { ②case .Rectangle(let width, let height):println("矩形的宽:\(width) 高:\(height)")case .Circle(let radius):println("圆形的半径:\(radius)")} ③}var figure = Figure.Rectangle(1024, 768) ④printFigure(figure) ⑤figure = .Circle(600) ⑥printFigure(figure) ⑦
上述代码使用前文声明的枚举类型Figure,为了能够反复调用,我们在代码第①行定义了一个函数。其中代码第②~③行使用了switch语句,为了从相关值中提取数据,可以在元组字段前面添加let或var。如果某个相关值元组中字段类型一致,需要全部提取,则可以在相关值前面添加let或var。我们可以使用如下方式修改Rectangle分支:
switch figure {case let .Rectangle( width, height):println("矩形的宽:\(width) 高:\(height)")case .Circle(let radius):println("圆形的半径:\(radius)")}
上述代码第④行var figure = Figure.Rectangle(1024, 768)是声明变量figure,并初始化为Rectangle类型的相关值。第⑤行代码是调用函数printFigure打印输出结果:
矩形的宽:1024 高:768
代码第⑥行figure = .Circle(600)初始化为Circle类型的相关值。第⑦行代码调用函数printFigure并打印输出结果:
圆形的半径:600
