枚舉

http://numbbbbb.gitbooks.io/-the-swift-programming-language-/content/chapter2/08_Enumerations.htmlhtml

 

本頁內容包含:ios

枚舉定義了一個通用類型的一組相關的值,使你能夠在你的代碼中以一個安全的方式來使用這些值。git

若是你熟悉 C 語言,你就會知道,在 C 語言中枚舉指定相關名稱爲一組整型值。Swift 中的枚舉更加靈活,沒必要給每個枚舉成員提供一個值。若是一個值(被認爲是「原始」值)被提供給每一個枚舉成員,則該值能夠是一個字符串,一個字符,或是一個整型值或浮點值。web

此外,枚舉成員能夠指定任何類型的相關值存儲到枚舉成員值中,就像其餘語言中的聯合體(unions)和變體(variants)。你能夠定義一組通用的相關成員做爲枚舉的一部分,每一組都有不一樣的一組與它相關的適當類型的數值。swift

在 Swift 中,枚舉類型是一等(first-class)類型。它們採用了不少傳統上只被類(class)所支持的特徵,例如計算型屬性(computed properties),用於提供關於枚舉當前值的附加信息, 實例方法(instance methods),用於提供和枚舉所表明的值相關聯的功能。枚舉也能夠定義構造函數(initializers)來提供一個初始成員值;能夠在原始的實現基礎上擴展它們的功能;能夠遵照協議(protocols)來提供標準的功能。安全

欲瞭解更多相關功能,請參見屬性(Properties)方法(Methods)構造過程(Initialization)擴展(Extensions)協議(Protocols)app

枚舉語法

使用enum關鍵詞而且把它們的整個定義放在一對大括號內:less

enum SomeEnumeration {
  // enumeration definition goes here
}

如下是指南針四個方向的一個例子:ide

enum CompassPoint {
  case North
  case South
  case East
  case West
}

一個枚舉中被定義的值(例如 NorthSouthEastWest)是枚舉的成員值(或者成員)。case關鍵詞代表新的一行成員值將被定義。函數

注意:
不像 C 和 Objective-C 同樣,Swift 的枚舉成員在被建立時不會被賦予一個默認的整數值。在上面的CompassPoints例子中,NorthSouthEastWest不是隱式的等於0123。相反的,這些不一樣的枚舉成員在CompassPoint的一種顯示定義中擁有各自不一樣的值。 

多個成員值能夠出如今同一行上,用逗號隔開:

enum Planet {
  case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}

每一個枚舉定義了一個全新的類型。像 Swift 中其餘類型同樣,它們的名字(例如CompassPointPlanet)必須以一個大寫字母開頭。給枚舉類型起一個單數名字而不是複數名字,以便於讀起來更加容易理解:

var directionToHead = CompassPoint.West

directionToHead的類型被推斷當它被CompassPoint的一個可能值初始化。一旦directionToHead被聲明爲一個CompassPoint,你可使用更短的點(.)語法將其設置爲另外一個CompassPoint的值:

directionToHead = .East

directionToHead的類型已知時,當設定它的值時,你能夠再也不寫類型名。使用顯式類型的枚舉值可讓代碼具備更好的可讀性。

匹配枚舉值和Switch語句

你能夠匹配單個枚舉值和switch語句:

directionToHead = .South
switch directionToHead {
case .North:
    println("Lots of planets have a north")
case .South:
    println("Watch out for penguins")
case .East:
    println("Where the sun rises")
case .West:
    println("Where the skies are blue")
}
// 輸出 "Watch out for penguins」

你能夠如此理解這段代碼:

「考慮directionToHead的值。當它等於.North,打印「Lots of planets have a north」。當它等於.South,打印「Watch out for penguins」。」

等等依次類推。

正如在控制流(Control Flow)中介紹,當考慮一個枚舉的成員們時,一個switch語句必須全面。若是忽略了.West這種狀況,上面那段代碼將沒法經過編譯,由於它沒有考慮到CompassPoint的所有成員。全面性的要求確保了枚舉成員不會被意外遺漏。

當不須要匹配每一個枚舉成員的時候,你能夠提供一個默認default分支來涵蓋全部未明確被提出的任何成員:

let somePlanet = Planet.Earth
switch somePlanet {
case .Earth:
    println("Mostly harmless")
default:
    println("Not a safe place for humans")
}
// 輸出 "Mostly harmless」

相關值(Associated Values)

上一小節的例子演示了一個枚舉的成員是如何被定義(分類)的。你能夠爲Planet.Earth設置一個常量或則變量,而且在以後查看這個值。無論怎樣,若是有時候可以把其餘類型的相關值和成員值一塊兒存儲起來會頗有用。這能讓你存儲成員值以外的自定義信息,而且當你每次在代碼中使用該成員時容許這個信息產生變化。

你能夠定義 Swift 的枚舉存儲任何類型的相關值,若是須要的話,每一個成員的數據類型能夠是各不相同的。枚舉的這種特性跟其餘語言中的可辨識聯合(discriminated unions),標籤聯合(tagged unions),或者變體(variants)類似。

例如,假設一個庫存跟蹤系統須要利用兩種不一樣類型的條形碼來跟蹤商品。有些商品上標有 UPC-A 格式的一維碼,它使用數字 0 到 9。每個條形碼都有一個表明「數字系統」的數字,該數字後接 10 個表明「標識符」的數字。最後一個數字是「檢查」位,用來驗證代碼是否被正確掃描:

其餘商品上標有 QR 碼格式的二維碼,它可使用任何 ISO8859-1 字符,而且能夠編碼一個最多擁有 2,953 字符的字符串:

對於庫存跟蹤系統來講,可以把 UPC-A 碼做爲三個整型值的元組,和把 QR 碼做爲一個任何長度的字符串存儲起來是方便的。

在 Swift 中,用來定義兩種商品條碼的枚舉是這樣子的:

enum Barcode {
  case UPCA(Int, Int, Int)
  case QRCode(String)
}

以上代碼能夠這麼理解:

「定義一個名爲Barcode的枚舉類型,它能夠是UPCA的一個相關值(IntIntInt),或者QRCode的一個字符串類型(String)相關值。」

這個定義不提供任何IntString的實際值,它只是定義了,當Barcode常量和變量等於Barcode.UPCABarcode.QRCode時,相關值的類型。

而後可使用任何一種條碼類型建立新的條碼,如:

var productBarcode = Barcode.UPCA(8, 85909_51226, 3)

以上例子建立了一個名爲productBarcode的新變量,而且賦給它一個Barcode.UPCA的相關元組值(8, 8590951226, 3)。提供的「標識符」值在整數字中有一個下劃線,使其便於閱讀條形碼。

同一個商品能夠被分配給一個不一樣類型的條形碼,如:

productBarcode = .QRCode("ABCDEFGHIJKLMNOP")

這時,原始的Barcode.UPCA和其整數值被新的Barcode.QRCode和其字符串值所替代。條形碼的常量和變量能夠存儲一個.UPCA或者一個.QRCode(連同它的相關值),可是在任何指定時間只能存儲其中之一。

像之前那樣,不一樣的條形碼類型可使用一個 switch 語句來檢查,然而此次相關值能夠被提取做爲 switch 語句的一部分。你能夠在switch的 case 分支代碼中提取每一個相關值做爲一個常量(用let前綴)或者做爲一個變量(用var前綴)來使用:

switch productBarcode {
case .UPCA(let numberSystem, let identifier, let check):
    println("UPC-A with value of \(numberSystem), \(identifier), \(check).")
case .QRCode(let productCode):
    println("QR code with value of \(productCode).")
}
// 輸出 "QR code with value of ABCDEFGHIJKLMNOP.」

若是一個枚舉成員的全部相關值被提取爲常量,或者它們所有被提取爲變量,爲了簡潔,你能夠只放置一個var或者let標註在成員名稱前:

switch productBarcode {
case let .UPCA(numberSystem, identifier, check):
    println("UPC-A with value of \(numberSystem), \(identifier), \(check).")
case let .QRCode(productCode):
    println("QR code with value of \(productCode).")
}
// 輸出 "QR code with value of ABCDEFGHIJKLMNOP."

原始值(Raw Values)

Associated Values小節的條形碼例子中演示了一個枚舉的成員如何聲明它們存儲不一樣類型的相關值。做爲相關值的替代,枚舉成員能夠被默認值(稱爲原始值)預先填充,其中這些原始值具備相同的類型。

這裏是一個枚舉成員存儲原始 ASCII 值的例子:

enum ASCIIControlCharacter: Character {
    case Tab = "\t"
    case LineFeed = "\n"
    case CarriageReturn = "\r"
}

在這裏,稱爲ASCIIControlCharacter的枚舉的原始值類型被定義爲字符型Character,並被設置了一些比較常見的 ASCII 控制字符。字符值的描述請詳見字符串和字符Strings and Characters部分。

注意,原始值和相關值是不相同的。當你開始在你的代碼中定義枚舉的時候原始值是被預先填充的值,像上述三個 ASCII 碼。對於一個特定的枚舉成員,它的原始值始終是相同的。相關值是當你在建立一個基於枚舉成員的新常量或變量時纔會被設置,而且每次當你這麼作得時候,它的值能夠是不一樣的。

原始值能夠是字符串,字符,或者任何整型值或浮點型值。每一個原始值在它的枚舉聲明中必須是惟一的。當整型值被用於原始值,若是其餘枚舉成員沒有值時,它們會自動遞增。

下面的枚舉是對以前Planet這個枚舉的一個細化,利用原始整型值來表示每一個 planet 在太陽系中的順序:

enum Planet: Int {
    case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}

自動遞增意味着Planet.Venus的原始值是2,依次類推。

使用枚舉成員的toRaw方法能夠訪問該枚舉成員的原始值:

let earthsOrder = Planet.Earth.toRaw()
// earthsOrder is 3

使用枚舉的fromRaw方法來試圖找到具備特定原始值的枚舉成員。這個例子經過原始值7識別Uranus

let possiblePlanet = Planet.fromRaw(7)
// possiblePlanet is of type Planet? and equals Planet.Uranus

然而,並不是全部可能的Int值均可以找到一個匹配的行星。正由於如此,fromRaw方法能夠返回一個可選的枚舉成員。在上面的例子中,possiblePlanetPlanet?類型,或「可選的Planet」。

若是你試圖尋找一個位置爲9的行星,經過fromRaw返回的可選Planet值將是nil

let positionToFind = 9
if let somePlanet = Planet.fromRaw(positionToFind) {
    switch somePlanet {
    case .Earth:
        println("Mostly harmless")
    default:
        println("Not a safe place for humans")
    }
} else {
    println("There isn't a planet at position \(positionToFind)")
}
// 輸出 "There isn't a planet at position 9

這個範例使用可選綁定(optional binding),經過原始值9試圖訪問一個行星。if let somePlanet = Planet.fromRaw(9)語句得到一個可選Planet,若是可選Planet能夠被得到,把somePlanet設置成該可選Planet的內容。在這個範例中,沒法檢索到位置爲9的行星,因此else分支被執行。

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息