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 Monday
case Tuesday
case Wednesday
case Thursday
case 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 = 0
case Tuesday = 1
case Wednesday = 2
case Thursday = 3
case Friday = 4
}
我们声明的WeekDays
枚举类型的原始值类型是Int
,需要给每个成员赋值,只要是Int
类型都可以,但是每个分支不能重复。我们还可以采用如下简便写法,只需要给第一个成员赋值即可,后面的成员值会依次加1。
enum WeekDays : Int {
case Monday = 0, Tuesday, Wednesday, Thursday, Friday
}
以下是完整的示例代码:
var day = WeekDays.Friday
func 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