diff --git a/source/chapter2/08_Enumerations.md b/source/chapter2/08_Enumerations.md index 1484694f..c0d7c949 100755 --- a/source/chapter2/08_Enumerations.md +++ b/source/chapter2/08_Enumerations.md @@ -11,20 +11,20 @@ - [相关值(Associated Values)](#associated_values) - [原始值(Raw Values)](#raw_values) -枚举定义了一个通用类型的一组相关的值,使你可以在你的代码中以一个安全的方式来使用这些值。 +*枚举*定义了一个通用类型的一组相关值,使你可以在你的代码中以一种安全的方式来使用这些值。 -如果你熟悉 C 语言,你就会知道,在 C 语言中枚举指定相关名称为一组整型值。Swift 中的枚举更加灵活,不必给每一个枚举成员提供一个值。如果一个值(被认为是“原始”值)被提供给每个枚举成员,则该值可以是一个字符串,一个字符,或是一个整型值或浮点值。 +如果你熟悉 C 语言,你就会知道,在 C 语言中枚举将枚举名和一个整型值相对应。Swift 中的枚举更加灵活,不必给每一个枚举成员提供一个值。如果给枚举成员提供一个值(称为“原始”值),则该值的类型可以是字符串,字符,或是一个整型值或浮点数。 此外,枚举成员可以指定任何类型的相关值存储到枚举成员值中,就像其他语言中的联合体(unions)和变体(variants)。你可以定义一组通用的相关成员作为枚举的一部分,每一组都有不同的一组与它相关的适当类型的数值。 -在 Swift 中,枚举类型是一等(first-class)类型。它们采用了很多传统上只被类(class)所支持的特征,例如计算型属性(computed properties),用于提供关于枚举当前值的附加信息, 实例方法(instance methods),用于提供和枚举所代表的值相关联的功能。枚举也可以定义构造函数(initializers)来提供一个初始成员值;可以在原始的实现基础上扩展它们的功能;可以遵守协议(protocols)来提供标准的功能。 +在 Swift 中,枚举类型是一等公民(first-class)。它们采用了很多传统上只被类(class)所支持的特征,例如计算型属性(computed properties),用于提供关于枚举当前值的附加信息, 实例方法(instance methods),用于提供和枚举所代表的值相关联的功能。枚举也可以定义构造函数(initializers)来提供一个初始值;可以在原始的实现基础上扩展它们的功能;可以遵守协议(protocols)来提供标准的功能。 -欲了解更多相关功能,请参见[属性(Properties)](10_Properties.html),[方法(Methods)](11_Methods.html),[构造过程(Initialization)](14_Initialization.html),[扩展(Extensions)](20_Extensions.html)和[协议(Protocols)](21_Protocols.html)。 +欲了解更多相关信息,请参见[属性(Properties)](10_Properties.html),[方法(Methods)](11_Methods.html),[构造过程(Initialization)](14_Initialization.html),[扩展(Extensions)](20_Extensions.html)和[协议(Protocols)](21_Protocols.html)。 ## 枚举语法 -使用`enum`关键词并且把它们的整个定义放在一对大括号内: +使用`enum`关键词来创建枚举并且把它们的整个定义放在一对大括号内: ```swift enum SomeEnumeration { @@ -43,10 +43,10 @@ enum CompassPoint { } ``` -一个枚举中被定义的值(例如 `North`,`South`,`East`和`West`)是枚举的***成员值***(或者***成员***)。`case`关键词表明新的一行成员值将被定义。 +一个枚举中被定义的值(例如 `North`,`South`,`East`和`West`)是枚举的*成员值*(或者*成员*)。`case`关键词表明新的一行成员值将被定义。 > 注意: -> 不像 C 和 Objective-C 一样,Swift 的枚举成员在被创建时不会被赋予一个默认的整数值。在上面的`CompassPoints`例子中,`North`,`South`,`East`和`West`不是隐式的等于`0`,`1`,`2`和`3`。相反的,这些不同的枚举成员在`CompassPoint`的一种显示定义中拥有各自不同的值。 +> 和 C 和 Objective-C 不同,Swift 的枚举成员在被创建时不会被赋予一个默认的整型值。在上面的`CompassPoints`例子中,`North`,`South`,`East`和`West`不会隐式地赋值为了`0`,`1`,`2`和`3`。相反的,这些不同的枚举成员在`CompassPoint`的一种显示定义中拥有各自不同的值。 多个成员值可以出现在同一行上,用逗号隔开: @@ -62,51 +62,51 @@ enum Planet { var directionToHead = CompassPoint.West ``` -`directionToHead`的类型被推断当它被`CompassPoint`的一个可能值初始化。一旦`directionToHead`被声明为一个`CompassPoint`,你可以使用更短的点(.)语法将其设置为另一个`CompassPoint`的值: +`directionToHead`的类型可以在它被`CompassPoint`的一个可能值初始化时推断出来。一旦`directionToHead`被声明为一个`CompassPoint`,你可以使用一个缩写语法(.)将其设置为另一个`CompassPoint`的值: ```swift directionToHead = .East ``` -`directionToHead`的类型已知时,当设定它的值时,你可以不再写类型名。使用显式类型的枚举值可以让代码具有更好的可读性。 +当`directionToHead`的类型已知时,再次为其赋值可以省略枚举名。使用显式类型的枚举值可以让代码具有更好的可读性。 ## 匹配枚举值和`Switch`语句 -你可以匹配单个枚举值和`switch`语句: +你可以使用`switch`语句匹配单个枚举值: ```swift directionToHead = .South switch directionToHead { case .North: - println("Lots of planets have a north") + print("Lots of planets have a north") case .South: - println("Watch out for penguins") + print("Watch out for penguins") case .East: - println("Where the sun rises") + print("Where the sun rises") case .West: - println("Where the skies are blue") + print("Where the skies are blue") } // 输出 "Watch out for penguins” ``` -你可以如此理解这段代码: +你可以这样理解这段代码: -“考虑`directionToHead`的值。当它等于`.North`,打印`“Lots of planets have a north”`。当它等于`.South`,打印`“Watch out for penguins”`。” +“判断`directionToHead`的值。当它等于`.North`,打印`“Lots of planets have a north”`。当它等于`.South`,打印`“Watch out for penguins”`。” -等等依次类推。 +等等以此类推。 -正如在[控制流(Control Flow)](05_Control_Flow.html)中介绍,当考虑一个枚举的成员们时,一个`switch`语句必须全面。如果忽略了`.West`这种情况,上面那段代码将无法通过编译,因为它没有考虑到`CompassPoint`的全部成员。全面性的要求确保了枚举成员不会被意外遗漏。 +正如在[控制流(Control Flow)](05_Control_Flow.html)中介绍的那样,在判断一个枚举类型的值时,`switch`语句必须穷举所有情况。如果忽略了`.West`这种情况,上面那段代码将无法通过编译,因为它没有考虑到`CompassPoint`的全部成员。强制性全部穷举的要求确保了枚举成员不会被意外遗漏。 -当不需要匹配每个枚举成员的时候,你可以提供一个默认`default`分支来涵盖所有未明确被提出的任何成员: +当不需要匹配每个枚举成员的时候,你可以提供一个默认`default`分支来涵盖所有未明确被提出的枚举成员: ```swift let somePlanet = Planet.Earth switch somePlanet { case .Earth: - println("Mostly harmless") + print("Mostly harmless") default: - println("Not a safe place for humans") + print("Not a safe place for humans") } // 输出 "Mostly harmless” ``` @@ -114,11 +114,11 @@ default: ## 相关值(Associated Values) -上一小节的例子演示了一个枚举的成员是如何被定义(分类)的。你可以为`Planet.Earth`设置一个常量或则变量,并且在之后查看这个值。不管怎样,如果有时候能够把其他类型的相关值和成员值一起存储起来会很有用。这能让你存储成员值之外的自定义信息,并且当你每次在代码中使用该成员时允许这个信息产生变化。 +上一小节的例子演示了如何定义(分类)枚举的成员。你可以为`Planet.Earth`设置一个常量或者变量,并且在赋值之后查看这个值。不管怎样,如果有时候能够把其他类型的*相关值*和成员值一起存储起来会很有用。这能让你存储成员值之外的自定义信息,并且当你每次在代码中使用该成员时允许这个信息产生变化。 你可以定义 Swift 的枚举存储任何类型的相关值,如果需要的话,每个成员的数据类型可以是各不相同的。枚举的这种特性跟其他语言中的可辨识联合(discriminated unions),标签联合(tagged unions),或者变体(variants)相似。 -例如,假设一个库存跟踪系统需要利用两种不同类型的条形码来跟踪商品。有些商品上标有 UPC-A 格式的一维码,它使用数字 0 到 9。每一个条形码都有一个代表“数字系统”的数字,该数字后接 10 个代表“标识符”的数字。最后一个数字是“检查”位,用来验证代码是否被正确扫描: +例如,假设一个库存跟踪系统需要利用两种不同类型的条形码来跟踪商品。有些商品上标有 UPC-A 格式的一维t条形码,它使用数字 0 到 9。每一个条形码都有一个代表“数字系统”的数字,该数字后接 5 个代表“生产代码”的数字,接下来是5位“产品代码”。最后一个数字是“检查”位,用来验证代码是否被正确扫描: @@ -128,28 +128,28 @@ default: 对于库存跟踪系统来说,能够把 UPC-A 码作为三个整型值的元组,和把 QR 码作为一个任何长度的字符串存储起来是方便的。 -在 Swift 中,用来定义两种商品条码的枚举是这样子的: +在 Swift 中,使用如下方式定义两种商品条码的枚举: ```swift enum Barcode { - case UPCA(Int, Int, Int) + case UPCA(Int, Int, Int, Int) case QRCode(String) } ``` 以上代码可以这么理解: -“定义一个名为`Barcode`的枚举类型,它可以是`UPCA`的一个相关值(`Int`,`Int`,`Int`),或者`QRCode`的一个字符串类型(`String`)相关值。” +“定义一个名为`Barcode`的枚举类型,它可以是`UPCA`的一个相关值(`Int`,`Int`,`Int`,`Int`),或者是`QRCode`的一个字符串类型(`String`)相关值。” 这个定义不提供任何`Int`或`String`的实际值,它只是定义了,当`Barcode`常量和变量等于`Barcode.UPCA`或`Barcode.QRCode`时,相关值的类型。 然后可以使用任何一种条码类型创建新的条码,如: ```swift -var productBarcode = Barcode.UPCA(8, 85909_51226, 3) +var productBarcode = Barcode.UPCA(8, 85909, 51226, 3) ``` -以上例子创建了一个名为`productBarcode`的新变量,并且赋给它一个`Barcode.UPCA`的相关元组值`(8, 8590951226, 3)`。提供的“标识符”值在整数字中有一个下划线,使其便于阅读条形码。 +以上例子创建了一个名为`productBarcode`的变量,并且赋给它一个`Barcode.UPCA`的相关元组值`(8, 85909, 51226, 3)`。 同一个商品可以被分配给一个不同类型的条形码,如: @@ -163,32 +163,32 @@ productBarcode = .QRCode("ABCDEFGHIJKLMNOP") ```swift switch productBarcode { -case .UPCA(let numberSystem, let identifier, let check): - println("UPC-A with value of \(numberSystem), \(identifier), \(check).") +case .UPCA(let numberSystem, let manufacturer, let product, let check): + print("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).") case .QRCode(let productCode): - println("QR code with value of \(productCode).") + print("QR code: \(productCode).") } -// 输出 "QR code with value of ABCDEFGHIJKLMNOP.” +// 输出 "QR code: ABCDEFGHIJKLMNOP." ``` 如果一个枚举成员的所有相关值被提取为常量,或者它们全部被提取为变量,为了简洁,你可以只放置一个`var`或者`let`标注在成员名称前: ```swift switch productBarcode { -case let .UPCA(numberSystem, identifier, check): - println("UPC-A with value of \(numberSystem), \(identifier), \(check).") +case let .UPCA(numberSystem, manufacturer, product, check): + print("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).") case let .QRCode(productCode): - println("QR code with value of \(productCode).") + print("QR code: \(productCode).") } -// 输出 "QR code with value of ABCDEFGHIJKLMNOP." +// 输出 "QR code: ABCDEFGHIJKLMNOP." ``` ## 原始值(Raw Values) -在[Associated Values](#raw_values)小节的条形码例子中演示了一个枚举的成员如何声明它们存储不同类型的相关值。作为相关值的替代,枚举成员可以被默认值(称为原始值)预先填充,其中这些原始值具有相同的类型。 +在[Associated Values](#raw_values)小节的条形码例子中演示了一个枚举的成员如何声明它们存储不同类型的相关值。作为相关值的另一种选择,枚举成员可以被默认值(称为原始值)赋值,其中这些原始值具有相同的类型。 -这里是一个枚举成员存储原始 ASCII 值的例子: +这里是一个枚举成员存储 ASCII 码的例子: ```swift enum ASCIIControlCharacter: Character { @@ -198,11 +198,11 @@ enum ASCIIControlCharacter: Character { } ``` -在这里,称为`ASCIIControlCharacter`的枚举的原始值类型被定义为字符型`Character`,并被设置了一些比较常见的 ASCII 控制字符。字符值的描述请详见字符串和字符[`Strings and Characters`](03_Strings_and_Characters.html)部分。 +在这里,`ASCIIControlCharacter`的枚举类型的原始值类型被定义为字符型`Character`,并被设置了一些比较常见的 ASCII 控制字符。字符值的描述请详见字符串和字符[`Strings and Characters`](03_Strings_and_Characters.html)部分。 -注意,原始值和相关值是不相同的。当你开始在你的代码中定义枚举的时候原始值是被预先填充的值,像上述三个 ASCII 码。对于一个特定的枚举成员,它的原始值始终是相同的。相关值是当你在创建一个基于枚举成员的新常量或变量时才会被设置,并且每次当你这么做得时候,它的值可以是不同的。 +注意,原始值和相关值是不相同的。原始值是当你开始定义枚举的时候被预先赋予的值,像上述三个 ASCII 码。对于一个特定的枚举成员,它的原始值始终是相同的。相关值在你在创建一个基于枚举成员的常量或变量时才会被设置,并且每次当你创建的时候,它的值可以是不同的。 -原始值可以是字符串,字符,或者任何整型值或浮点型值。每个原始值在它的枚举声明中必须是唯一的。当整型值被用于原始值,如果其他枚举成员没有值时,它们会自动递增。 +原始值可以是字符串,字符,或者任何整型值或浮点型值。每个原始值在它的枚举声明中必须是唯一的。当使用整型值作为原始值时,如果其他枚举成员没有值,它们会自动递增。 下面的枚举是对之前`Planet`这个枚举的一个细化,利用原始整型值来表示每个 planet 在太阳系中的顺序: @@ -212,7 +212,7 @@ enum Planet: Int { } ``` -自动递增意味着`Planet.Venus`的原始值是`2`,依次类推。 +自动递增意味着`Planet.Venus`的原始值是`2`,以此类推。 使用枚举成员的`rawValue`属性可以访问该枚举成员的原始值: @@ -220,15 +220,22 @@ enum Planet: Int { let earthsOrder = Planet.Earth.rawValue // earthsOrder is 3 ``` +### 使用原始值来初始化(Initializing from a Raw Value) -通过参数为`rawValue`构造函数创建特定原始值的枚举。这个例子通过原始值`7`识别`Uranus`: +如果你使用原始值的方式创建一个枚举类型,这个枚举将自动获得一个包含原始值参数(参数名为rawValue)的构造器并返回相应的枚举类型或者nil。你可以使用这个构造器来创建新的枚举成员。 + +下面这个例子通过原始值`7`创建了`Uranus`枚举类型: ```swift let possiblePlanet = Planet(rawValue: 7) // possiblePlanet is of type Planet? and equals Planet.Uranus ``` -然而,并非所有可能的`Int`值都可以找到一个匹配的行星。正因为如此,构造函数可以返回一个***可选***的枚举成员。在上面的例子中,`possiblePlanet`是`Planet?`类型,或“可选的`Planet`”。 +然而,并非所有可能的`Int`值都可以找到一个匹配的行星。正因为如此,构造函数可以返回一个*可选*的枚举成员。在上面的例子中,`possiblePlanet`是`Planet?`类型,或称为“可选的`Planet`”。 + +> 注意: +> 使用原始值构造器是可失败构造器,因为并不是所有的原始值都会返回一个对应的枚举成员。欲了解更多相关信息,请参见[可失败构造器(Failable Initializers)](TODO) + 如果你试图寻找一个位置为9的行星,通过参数为`rawValue`构造函数返回的可选`Planet`值将是`nil`: @@ -237,15 +244,14 @@ let positionToFind = 9 if let somePlanet = Planet(rawValue: positionToFind) { switch somePlanet { case .Earth: - println("Mostly harmless") + print("Mostly harmless") default: - println("Not a safe place for humans") + print("Not a safe place for humans") } } else { - println("There isn't a planet at position \(positionToFind)") + print("There isn't a planet at position \(positionToFind)") } // 输出 "There isn't a planet at position 9 ``` -这个范例使用可选绑定(optional binding),通过原始值`9`试图访问一个行星。`if let somePlanet = Planet(rawValue: 9)`语句获得一个可选`Planet`,如果可选`Planet`可以被获得,把`somePlanet`设置成该可选`Planet`的内容。在这个范例中,无法检索到位置为`9`的行星,所以`else`分支被执行。 - +这个范例使用可选绑定(optional binding),通过原始值`9`试图取得一个行星的引用。`if let somePlanet = Planet(rawValue: 9)`语句获得一个可选`Planet`,如果可选`Planet`可以被获得,把`somePlanet`设置成该可选`Planet`的内容。在这个范例中,无法检索到位置为`9`的行星,所以`else`分支被执行。