加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

Swift4.2语言规范(十) 枚举

发布时间:2020-12-14 05:11:41 所属栏目:百科 来源:网络整理
导读:一个 枚举 定义了一个通用型的一组相关的值,使你在你的代码中的一个类型安全的方式这些值来工作。 如果您熟悉C,您将知道C枚举将相关名称分配给一组整数值。 Swift中的枚举更加灵活,并且不必为枚举的每个案例提供值。 如果一个值(被称为“原始”的值) 被

一个枚举定义了一个通用型的一组相关的值,使你在你的代码中的一个类型安全的方式这些值来工作。

如果您熟悉C,您将知道C枚举将相关名称分配给一组整数值。Swift中的枚举更加灵活,并且不必为枚举的每个案例提供值。如果一个值(被称为“原始”的值)提供给每个枚举的情况下,该值可以是一个字符串,一个字符,或任何整数的值或者浮点型。

或者,枚举情况可以指定要与每个不同的案例值一起存储任何类型的关联值,就像其他语言中的联合或变体一样。您可以将一组通用的相关案例定义为一个枚举的一部分,每个枚举都有一组与之关联的适当类型的不同值。

Swift中的枚举本身就是一流的类型。它们采用传统上仅由类支持的许多功能,例如计算属性以提供有关枚举当前值的其他信息,以及实例方法以提供与枚举所代表的值相关的功能。枚举还可以定义初始化器以提供初始案例值;?可以扩展以扩展其原始实现之外的功能;?并且可以符合协议以提供标准功能。

有关这些功能的更多信息,请参阅属性方法初始化扩展协议

枚举语法

您将使用enum关键字引入枚举,并将其整个定义放在一对大括号中:

1 enum SomeEnumeration {
2     // enumeration definition goes here
3 }

以下是指南针的四个要点的示例:

1 enum CompassPoint {
2     case north
3     case south
4     case east
5     case west
6 }

在枚举定义的值(例如northsoutheast,和west)是其枚举的情况下您可以使用该case关键字来引入新的枚举案例。

注意

与C和Objective-C不同,Swift枚举情况在创建时未分配默认整数值。CompassPoint上面的例子,northsoutheastwest不等于隐式0123相反,不同的枚举案例本身就是完全成熟的值,具有明确定义的类型CompassPoint

多个案例可以出现在一行中,以逗号分隔:

1 enum Planet {
2     case mercury,venus,earth,mars,jupiter,saturn,uranus,neptune
3 }

每个枚举定义都定义了一个新类型。与Swift中的其他类型一样,它们的名称(例如CompassPointPlanet)应以大写字母开头。给出枚举类型单数而不是复数名称,以便它们看起来不言自明:

var directionToHead = CompassPoint.west

directionToHead当使用其中一个可能的值初始化时推断出类型CompassPoint一旦directionToHead声明为a?CompassPoint,您可以CompassPoint使用较短的点语法将其设置为不同的值:

directionToHead = .east

类型directionToHead已知,因此您可以在设置其值时删除该类型。在使用显式类型的枚举值时,这会产生高度可读的代码。

使用Switch语句匹配枚举值

您可以将单个枚举值与switch语句匹配

 1 directionToHead = .south
 2 switch directionToHead {
 3 case .north:
 4     print("Lots of planets have a north")
 5 case .south:
 6     print("Watch out for penguins")
 7 case .east:
 8     print("Where the sun rises")
 9 case .west:
10     print("Where the skies are blue")
11 }
12 // Prints "Watch out for penguins"

您可以将此代码读作:

“依据值directionToHead在它等于的情况下.north,打印在它等于的情况下,打印。“"Lots?of?planets?have?a?north".south"Watch?out?for?penguins"

…等等。

控制流程中所述switch在考虑枚举的情况时语句必须是详尽的。如果省略casefor?.west,则此代码不会编译,因为它不考虑完整的CompassPoint案例列表要求详尽无遗确保枚举案例不会被意外遗漏。

如果不适合case为每个枚举案例提供一个,您可以提供一个default案例来涵盖未明确解决的任何案例:

1 let somePlanet = Planet.earth
2 switch somePlanet {
3 case .earth:
4     print("Mostly harmless")
5 default:
6     print("Not a safe place for humans")
7 }
8 // Prints "Mostly harmless"

迭代枚举案例

对于某些枚举,拥有所有枚举的案例集合很有用。您可以通过在枚举名称后面写入来启用它Swift将所有案例的集合公开为枚举类型属性。这是一个例子::?CaseIterableallCases

1 enum Beverage: CaseIterable {
2     case coffee,tea,juice
3 }
4 let numberOfChoices = Beverage.allCases.count
5 print("(numberOfChoices) beverages available")
6 // Prints "3 beverages available"

在上面的示例中,您编写Beverage.allCases以访问包含Beverage枚举的所有情况的集合您可以allCases像任何其他集合一样使用- 集合的元素是枚举类型的实例,因此在这种情况下,它们是Beverage值。上面的示例计算了有多少个案例,下面的示例使用for循环来迭代所有案例。

1 for beverage in Beverage.allCases {
2     print(beverage)
3 }
4 // coffee
5 // tea
6 // juice

?

上面示例中使用的语法将枚举标记为符合CaseIterable协议。有关协议的信息,请参阅协议

关联值

上一节中的示例显示了枚举的情况本身是如何定义(和类型化)的值。您可以将常量或变量设置为Planet.earth,并在以后检查此值。但是,有时能够在这些案例值旁边存储其他类型的关联值。这使您可以存储其他自定义信息以及案例值,并允许每次在代码中使用该案例时此信息都会有所不同。

您可以定义Swift枚举以存储任何给定类型的关联值,并且如果需要,每种枚举情况的值类型可以不同。与这些类似的枚举称为区别联合标记联合其他编程语言中的变体

例如,假设库存跟踪系统需要通过两种不同类型的条形码跟踪产品。有些产品标有UPC格式的1D条形码,使用数字0来表示9每个条形码都有一个“数字系统”数字,后面跟着五个“制造商代码”数字和五个“产品代码”数字。然后是“检查”数字,以验证代码是否已正确扫描:

其他产品使用QR码格式的二维条码进行标记,可以使用任何ISO 8859-1字符,并且可以编码长达2,953个字符的字符串:

对于库存跟踪系统来说,能够将UPC条形码存储为四个整数的元组以及QR码条形码作为任意长度的字符串将是方便的。

在Swift中,定义任一类型的产品条形码的枚举可能如下所示:

1 enum Barcode {
2     case upc(Int,Int,Int)
3     case qrCode(String)
4 }

这可以理解为:

“定义枚举类型Barcode,它可以采取任何的值upc与式(的相关值IntIntIntInt),或的值qrCode与类型的相关联的值String。”

此定义不提供任何实际IntString值 - 它只定义常量和变量在等于时可以存储的关联值类型BarcodeBarcode.upcBarcode.qrCode

然后可以使用以下任一类型创建新的条形码:

var productBarcode = Barcode.upc(8,85909,51226,3)

此示例创建一个名为的新变量,productBarcode并为其赋值Barcode.upc一个相关的元组值(8,?85909,?51226,?3)

可以为同一产品分配不同类型的条形码:

productBarcode = .qrCode("ABCDEFGHIJKLMNOP")

此时,原始Barcode.upc值及其整数值将替换为new?Barcode.qrCode及其字符串值。类型的常量和变量Barcode可以存储a?.upc或a?.qrCode(及其相关值),但它们只能在任何给定时间存储其中一个。

与以前一样,可以使用switch语句检查不同的条形码类型。但是,这次可以将关联值提取为switch语句的一部分。您将每个关联值提取为常量(带let前缀)或变量(带var前缀),以便在switch案例正文中使用:

1 switch productBarcode {
2 case .upc(let numberSystem,let manufacturer,let product,let check):
3     print("UPC: (numberSystem),(manufacturer),(product),(check).")
4 case .qrCode(let productCode):
5     print("QR code: (productCode).")
6 }
7 // Prints "QR code: ABCDEFGHIJKLMNOP."

如果枚举案例的所有关联值都被提取为常量,或者如果所有这些值都被提取为变量,则可以在案例名称前面放置单个varlet注释,以简洁起见:

1 switch productBarcode {
2 case let .upc(numberSystem,manufacturer,product,check):
3     print("UPC : (numberSystem),(check).")
4 case let .qrCode(productCode):
5     print("QR code: (productCode).")
6 }
7 // Prints "QR code: ABCDEFGHIJKLMNOP."

默认值

“?关联值?”中的条形码示例显示了枚举的情况如何声明它们存储了不同类型的关联值。作为关联值的替代,枚举情况可以预先填充默认值(称为原始值),它们都是相同的类型。

这是一个存储原始ASCII值和命名枚举情况的示例:

1 enum ASCIIControlCharacter: Character {
2     case tab = "t"
3     case lineFeed = "n"
4     case carriageReturn = "r"
5 }

这里,被调用枚举的原始值ASCIIControlCharacter被定义为类型Character,并被设置为一些更常见的ASCII控制字符。字符串和字符Character中描述了值

原始值可以是字符串,字符或任何整数或浮点数类型。每个原始值在其枚举声明中必须是唯一的。

注意

原始值是一样的关联值。当您首次在代码中定义枚举时,原始值将设置为预填充值,如上面的三个ASCII代码。特定枚举情况的原始值始终相同。根据枚举的情况创建新常量或变量时,将设置关联值,每次执行此操作时可能会有所不同。

隐含地分配默认值

当您使用存储整数或字符串原始值的枚举时,您不必为每个案例显式分配原始值。如果不这样做,Swift将自动为您分配值。

例如,当整数用于原始值时,每个案例的隐含值比前一个案例多一个。如果第一种情况没有设置值,则其值为0

下面的枚举是先前Planet枚举的细化,整数原始值表示来自太阳的每个行星的顺序:

1 enum Planet: Int {
2     case mercury = 1,neptune
3 }

在上面的示例中,Planet.mercury具有显式原始值1Planet.venus具有隐式原始值2,等等。

当字符串用于原始值时,每个案例的隐含值是该案例名称的文本。

下面的枚举是前面CompassPoint枚举的细化,字符串原始值表示每个方向的名称:

1 enum CompassPoint: String {
2     case north,south,east,west
3 }

在上面的示例中,CompassPoint.south隐含原始值为"south",依此类推。

您可以使用其rawValue属性访问枚举案例的原始值

1 let earthsOrder = Planet.earth.rawValue
2 // earthsOrder is 3
3 
4 let sunsetDirection = CompassPoint.west.rawValue
5 // sunsetDirection is "west"

默认值初始化

如果使用默认值类型定义枚举,则枚举会自动接收一个初始值设定项,该初始值设定项接受默认值类型的值(作为参数调用rawValue)并返回枚举大小写或nil您可以使用此初始化程序尝试创建枚举的新实例。

这个例子从原始值中识别天王星7

1 let possiblePlanet = Planet(rawValue: 7)
2 // possiblePlanet is of type Planet? and equals Planet.uranus

然而,并非所有可能的Int值都会找到匹配的行星。因此,原始值初始值设定项始终返回可选的枚举大小写。在上面的示例中,possiblePlanet是类型Planet?或“可选”?Planet

注意

原始值初始化程序是一个可用的初始化程序,因为并非每个原始值都将返回枚举情况。有关更多信息,请参阅Failable Initializers

如果您尝试查找位置为的行星,则原始值初始值设定项返回11的可选Planet值将为nil

 1 let positionToFind = 11
 2 if let somePlanet = Planet(rawValue: positionToFind) {
 3     switch somePlanet {
 4     case .earth:
 5         print("Mostly harmless")
 6     default:
 7         print("Not a safe place for humans")
 8     }
 9 } else {
10     print("There isn‘t a planet at position (positionToFind)")
11 }
12 // Prints "There isn‘t a planet at position 11"

此示例使用可选绑定尝试访问原始值为的行星11该语句创建一个可选项,并设置为该可选项的值(如果可以检索它)。在这种情况下,无法检索位置为的行星,因此执行分支。

if let somePlanet = Planet(rawValue: 11)PlanetsomePlanetPlanet11else

递归枚举

递归枚举是具有枚举作为一个或一个以上的枚举案件相关联的值的另一个实例的枚举。您通过indirect在它之前写入来指示枚举情况是递归的,这告诉编译器插入必要的间接层。

例如,这是一个存储简单算术表达式的枚举:

1 enum ArithmeticExpression {
2     case number(Int)
3     indirect case addition(ArithmeticExpression,ArithmeticExpression)
4     indirect case multiplication(ArithmeticExpression,ArithmeticExpression)
5 }

您还可以indirect在枚举开始之前编写,以便为具有关联值的所有枚举案例启用间接:

1 indirect enum ArithmeticExpression {
2     case number(Int)
3     case addition(ArithmeticExpression,ArithmeticExpression)
4     case multiplication(ArithmeticExpression,ArithmeticExpression)
5 }

此枚举可以存储三种算术表达式:普通数字,两个表达式的相加以及两个表达式的相乘。additionmultiplication案件有关联的,同时也是算术表达式,这些相关的值有可能嵌套的表达式的值。例如,表达式在乘法的右侧有一个数字,在乘法的左侧有另一个表达式。因为数据是嵌套的,用于存储数据的枚举也需要支持嵌套 - 这意味着枚举需要递归。下面的代码显示了为以下内容创建递归枚举(5?+?4)?*?2ArithmeticExpression(5?+?4)?*?2

1 let five = ArithmeticExpression.number(5)
2 let four = ArithmeticExpression.number(4)
3 let sum = ArithmeticExpression.addition(five,four)
4 let product = ArithmeticExpression.multiplication(sum,ArithmeticExpression.number(2))

递归函数是处理具有递归结构的数据的简单方法。例如,这是一个计算算术表达式的函数:

 1 func evaluate(_ expression: ArithmeticExpression) -> Int {
 2     switch expression {
 3     case let .number(value):
 4         return value
 5     case let .addition(left,right):
 6         return evaluate(left) + evaluate(right)
 7     case let .multiplication(left,right):
 8         return evaluate(left) * evaluate(right)
 9     }
10 }
11 
12 print(evaluate(product))
13 // Prints "18"

此函数只需返回相关值即可评估普通数字。它通过评估左侧的表达式,评估右侧的表达式,然后将它们相加或相乘来评估加法或乘法。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读