enum 儲存相關聯資料的 associated value

Swift 的 enum 有個特別的 associated value 語法,它將讓 enum 的 case 具有儲存相關聯資料的特異功能。接下來讓我們透過實際的例子好好認識它吧。

儲存相關聯資料的 associated value

假設我們定義 enum Status 記錄上班是否準時。在 Status 裡我們宣告兩種成員, onTime 表示準時抵達的乖寶寶,delayed 表示人在江湖,身不由己造成的遲到。

我們希望 delayed 的 case 不只能說明遲到,還能告訴我們遲到了幾分幾秒。因此在宣告 case delayed 時,我們運用能儲存相關聯資料的 associated value 語法。

associated value 語法的格式如下

我們在 delayed 後接 ( ),( ) 裡宣告的參數為儲存資料的 associated value。在這個例子,我們宣告 2 個相關聯的 value, minute 和 second,型別都是 Int,用來儲存遲到了幾分幾秒。

enum Status {
case onTime
case delayed(minute: Int, second: Int)
}

要如何設定 associated value 的內容呢? 很簡單,在產生 enum 資料時,於 Status.delayed 後接上 ( ),於 ( ) 裡傳入 associated value 的相關內容。在這個例子,參數分別為 minute 和 second,我們將 minute 指定為 10,second 指定為 20,表示遲到 10 分 20 秒。

let todayStatus = Status.delayed(minute: 10, second: 20)

associated value & switch

當 associated value 和最佳拍檔 switch 結合時,兩者擦出的火花,將產生更大的威力。比方以下例子,我們利用 switch 判斷是否準時。若是完美 onTime,老闆將龍心大悅,幫我們加薪一百萬。若是不幸遲到了,那事情可大可小。20 分 20 秒內可花錢消災,只需扣薪一百萬。否則的話,老闆將火山爆發,我們只好默默打包,準備尋找下一個工作的棲身地。

func checkStatus(status: Status) {
switch status {
case .onTime:
print("加薪一百萬")
case .delayed(let minute, let second):
if minute > 20, second > 20 {
print("東西收一收, 明天不用來了")
} else {
print("扣薪一百萬")
}
}
}

當我們在 switch 裡和 case delayed 比對時,可從 .delayed(let minute, let second) 的參數 minute 和 second 得知遲到的時間。

想偷懶的話,甚至還有更精簡的寫法,我們可將參數宣告的 let 移到前面,變成 case let .delayed(minute, second)

func checkStatus(status: Status) {
switch status {
case .onTime:
print("加薪一百萬")
case let .delayed(minute, second):
if minute > 20, second > 20 {
print("東西收一收, 明天不用來了")
} else {
print("扣薪一百萬")
}
}
}

在 switch 裡利用 case 比對 associated value 時,我們也可結合 where,直接將相關的比較用 where 串接。比方以下例子利用 where minute > 0 判斷遲到超過一分鐘時,東西收一收,明天不用來了。另外由於 second 用不到,所以以下例子裡我們使用 delayed(minute, _),將 second 以 _ 代替。

func checkStatus(status: Status) {
switch status {
case .onTime:
print("加薪一百萬")
case let .delayed(minute, _) where minute > 1:
print("東西收一收, 明天不用來了")
default:
print("扣薪一百萬")
}
}

當我們不需要判斷 associated value 時,也可以直接省略它們,因此以下例子寫成 case .delayed,省略後面的 ( )。

func checkStatus(status: Status) {
switch status {
case .onTime:
print("加薪一百萬")
case .delayed:
print("東西收一收, 明天不用來了")
}
}

省略參數名的 associated value

在宣告 associated value 的參數時,我們也可以只宣告型別,省略參數名。

enum Status {
case onTime
case delayed(Int, Int)
}

let todayStatus = Status.delayed(20, 20)

raw value & associated value 的 PK

跟 enum 的另一個 raw value 功能相比,associated value 有以下幾點勝出之處:

  • 可在產生 enum 資料時設定 associated value 的內容,raw value 只能在定義 enum 時指定,之後不能更改。
  • 能和多個 value 相關聯,享有如同一夫多妻的福利,不像 raw value 只能一對一,遵守傳統的一夫一妻制。
  • 相關聯的 value 可以是任何型別,甚至是類別生成的物件。raw value 只能搭配 String,Int 等基本型別。

Apple 官網的 associated value 範例

enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}

以 enum 定義 Barcode,upc 儲存 4 個數字,qrCode 儲存字串。

左邊是 UPC,右邊是 qrCode
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
print("QR code: \(productCode).")
}

Swift 的 associated value 範例: Optional

--

--

彼得潘的 iOS App Neverland
彼得潘的 Swift iOS App 開發問題解答集

彼得潘的iOS App程式設計入門,文組生的iOS App程式設計入門講師,彼得潘的 Swift 程式設計入門,App程式設計入門作者,http://apppeterpan.strikingly.com