Ben Morrow
作为一名开发人员,有一天你会意识到自己整日沉迷于电脑。世界那么大你也想去看看。因此,你需要一张你要去的地方的地图。所以你决定开发一个:定制地图应用程序。(又回到原点了)
编码时,你想把几个主要的方向作为变量,那么怎么表示这几个方向呢?
你可以将每个值表示为一个整数,如下所示:
•北:1
•南:2
•东:3
•西:4
但是,如果你的用户以不同的顺序考虑方向,这就让人很迷茫了。“3是什么来着?”为了缓解这种情况,你可以将值表示为字符串,如下所示:
• North: "north"
• South: "south"
• East: "east"
• West: "west"
但是,字符串的的值可以是任何字符串。如果你的应用程序接收到了“up”而不是“north”,你怎么办?此外,当你在编写代码时,很容易误写,比如“ north”写成成“nrth”。
如果有一种方法可以创建一组相关的值,那不是很好吗?如果你发现自己有这方面的需要,那么你需要使用枚举。
枚举是定义相关值的列表并且值类型相同,让你以类型安全的方式处理值。如果你的代码期望一组Direction方向,而你试图以错误数字或拼写错误的方向(如“Souuth”)传递,编译器将会报错。
除了基本方向,其他相关值的例子有颜色(黑色、红色、蓝色)、卡片套装(红心、黑桃、俱乐部、钻石)和角色(管理员、编辑、读者)。

Swift中的枚举比其他语言(如C或Objective-C)要强大得多。它们与结构和类共享特性。枚举也可以有方法和计算属性。
在本章中,你将了解枚举是如何工作的以及它们何时有用。作为奖励,你最终会发现在可选值的秘密。提示:它们是用枚举实现的!
第一个枚举
你的挑战是:建立一个能判断某月是什么学期。比如,5月是春季学期,八月是秋季学期,解决这个问题的一种方法是使用一个字符串数组,并将它们与一个switch语句的学期semesters匹配:
let months = ["January", "February", "March", "April", "May",
"June", "July", "August", "September", "October",
"November", "December"]
func semester(for month: String) -> String {
switch month {
case "August", "September", "October", "November", "December":
return "Autumn"
case "January", "February", "March", "April", "May":
return "Spring"
default:
return "Not in the school year"
}
}
semester(for: "April") // Spring
在playground上运行这段代码,你可以看到函数正确地返回“Spring”。但是正如我在引言中提到的那样,你很容易就会将一个字符串写错。解决这个问题的更好方法是使用枚举。
声明一个枚举
若要声明枚举,要使用case子句,列出所有可能的成员值:
enum Month {
case January
case February
case march
case April
case may
case June
case July
case august
case september
case October
case november
case december
}
此代码创建一个名为Month的枚举,其中包含12个可能的成员值。通常被接受的最佳做法是,每个成员值的第一个字母小写,就像一个属性一样。
通过将case子句用逗号分隔,可以简化代码:
enum Month {
case january, february, march, april, may, june, july, august,
september, october, november, december
}
这看起来很时尚,也很简单。
在函数中使用枚举
你可以重写这个函数来判断这个学期,让它使用枚举值而不是字符串匹配:
func semester(for month: Month) -> String {
switch month {
case Month.august, Month.september, Month.october,
Month.november, Month.december:
return "Autumn"
case Month.january, Month.february, Month.march, Month.april,
Month.may:
return "Spring"
default:
return "Not in the school year"
}
}
由于Swift是强类型的,并且使用类型推断,所以你可以删除枚举名称来简化这个semester(for:)。保持点前缀,删除枚举名称,但是这种简化方式只能在编译器已经知道类型的地方使用,如下图所示:
func semester(for month: Month) -> String {
switch month {
case .august, .september, .october, .november, .december:
return "Autumn"
case .january, .february, .march, .april, .may:
return "Spring"
default:
return "Not in the school year"
}
}
还记得吗?switch语句必须写出所有的可能性。否则编译器会警告你。当你的对比的是字符串元素时,你需要一个默认情况,因为不可能创建每个可能的字符串值的case。但是,枚举的值集是有限的。因此,如果你对枚举的每个成员值都有case,你可以安全地删除switch语句的默认情况:
func semester(for month: Month) -> String {
switch month {
case .august, .september, .october, .november, .december:
return "Autumn"
case .january, .february, .march, .april, .may:
return "Spring"
case .june, .July:
return "Summer"
}
}
这样的代码更加易读。删除默认值还有一个巨大的好处。如果在将来的更新中有人添加了.undecember或. duodecember到Month枚举,编译器会在使用这个枚举的switch语句中警告,因为它没有写出所有可能的值,需要你处理这个新增的情况case。
你可以在playground测试这个功能:
var month = Month.april
semester(for: month) // "Spring"
month = .september
semester(for: month) // "Autumn"
month的变量声明使用完整的枚举类型和值。在第二个值分配中,使用了简化。 .september,因为编译器已经知道类型。最后,你将两个month都传递给semester(for:),其中switch语句分别返回字符串“Spring”和“Autumn”。
代码补全防止拼写错误
使用枚举而不使用字符串的另一个好处是,在你的成员值中永远不会出现输入错误。Xcode提供代码补全功能:

如果你拼写错了一个枚举值,编译器会报一个错误,这样你就不会在看不到错误的情况下走得太远了:

网友评论