2019/05/04

Swift - Enum Associated Value

Swift Enum 簡介

Swift語言的Enum使用方式,與C++,Java等常見的程式語言無太大差異,範例如下:

//enum定義
enum MyItem {
    case localItem
    case cloudItem
}

//使用enum
var myItem = MyItem.cloudItem
 
switch myItem {
    case .localItem:
    // ...
    case .cloudItem:
    // ...
}

Associated value

Associated value則是Swift語言的Enum中,比較特別的一個功能。你可以讓一個enum變數額外地儲存多個其他的值,且幾乎可以是任意型別。

舉例來說,我想要讓cloudItem儲存一個代表網址的字串;而localItem則儲存了一個代表檔案路徑的字串,以及代表檔案大小的Int數值:

//enum定義
enum MyItem {
    //儲存在雲端的項目,只有能存取該項目的URL網址
    case cloudItem(String) // URL String
    
    //儲存在本地端的項目,有檔案路徑以及檔案大小的資訊
    case localItem(String, Int) // File path, file size
}

//使用enum與其associated value
let myItem1 = MyItem.networkItem("https://www.xxx.net")
let myItem2 = MyItem.networkItem("https://www.ooo.net")
//...
let myItem3 = MyItem.localItem("/home/username/xxx.txt",1024)

在switch case中取得associated value

在使用switch case判斷enum變數的類型時,
可在後面加上括號,並寫上letvar以及變數名稱,
以取出associated value,範例如下:


switch myItem1 {
    case .localItem(let filepath, let filesize):
        //...
    case .cloudItem(let urlString):
        //...
}
    

在switch case以外的場合取得associated value

使用if

若只想要針對特定的enum取出associated value,也可以改使用if case

if case .localItem(let filepath, let filesize) = myItem3 {
    //...
}
    

if caselet/var可提到前面:

if case let .localItem(filepath, filesize) = myItem3 {
    //...
}
    

if case的後面,可加上逗號,並於逗號後面做額外的條件判斷:

if case let .localItem(filepath, filesize) = myItem3, filesize >= 128 {
    //...
}
    

使用for

除了if case之外,還有for迴圈也可以取得associated value,範例如下

let myItems:[MyItem] = [
    .networkItem("https://www.xxx.net"),
    .localItem("/home/username/xxx.txt",1024),
    .networkItem("https://www.yyy.net"),
    //...
]
  

for case let .localItem(filepath, filesize) in myItems {
    //...
}

for case可使用where加上條件判斷

for case let .localItem(filepath, filesize) in myItems where filesize >= 128 {
    //...
}

使用guard

同樣屬於條件判斷類型的guard,也可以使用case取得associated value並做判斷

guard case let .localItem(filepath, filesize) = myItem3, filesize >= 128 else {
    //return or etc ...
}
    

Recursive Enumerations

由於associated value可以是任何型態的變數,因此也可以是自身型態的enum,進而形成了Recursive Enumerations

要特別注意的是,associated value中用到的變數型態有自身的enum的話,要加上indirect

enum MyItem {
    case cloudItem(String) // URL String
    
    case localItem(String, Int) // File path, file size
    
    //因為associated value用到了MyItem本身,所以要加上indirect
    indirect case pairedItem(MyItem, MyItem)
}

indirect也可以加在自身的enum宣告前面,這樣即可不用在每個有使用到associated value的項目中再寫一次

//為整個enum加上indirect
indirect enum MyItem {
    case cloudItem(String) // URL String
    
    case localItem(String, Int) // File path, file size
    
    //此處可不用再寫indirect
    case pairedItem(MyItem, MyItem)
    //此處可不用再寫indirect
    case multipleItems([MyItem])
}

Reference

Enumeration - The Swift Programming Language (Swift 5)

Pattern Matching, Part 4: if case, guard case, for case- Crunchy Development

沒有留言:

張貼留言