深入解析Swift中switch語句對case的數(shù)據(jù)類型匹配的支持
Swift可以對switch中不同數(shù)據(jù)類型的值作匹配判斷:
var things = Any[]() things.append(0) things.append(0.0) things.append(42) things.append(3.14159) things.append("hello") things.append((3.0, 5.0)) things.append(Movie(name:"Ghostbusters", director:"Ivan Reitman")) for thing in things { switch thing { case 0 as Int: println("zero as an Int") case 0 as Double: println("zero as a Double") case let someInt as Int: println("an integer value of (someInt)") case let someDouble as Double where someDouble > 0: println("a positive double value of (someDouble)") case is Double: println("some other double value that I don't want to print") case let someString as String: println("a string value of "(someString)"") case let (x, y) as (Double, Double): println("an (x, y) point at (x), (y)") case let movie as Movie: println("a movie called '(movie.name)', dir. (movie.director)") default: println("something else") } } // zero as an Int // zero as a Double // an integer value of 42 // a positive double value of 3.14159 // a string value of"hello" // an (x, y) point at 3.0, 5.0 // a movie called 'Ghostbusters', dir. Ivan Reitman
這里面會根據(jù)thing的值進行匹配,到對應(yīng)的case當中。
今天突然想到一個問題,讓我覺得有必要總結(jié)一下switch語句。我們知道swift中的switch,遠比C語言只能比較整數(shù)強大得多,但問題來了,哪些類型可以放到switch中比較呢,對象可以比較么?
官方文檔對switch的用法給出了這樣的解釋:
Cases can match many different patterns, including interval matches, tuples, and casts to a specific type.
也就是說除了最常用的比較整數(shù)、字符串等等之外,switch還可以用來匹配范圍、元組,轉(zhuǎn)化成某個特定類型等等。但文檔里這個including用的實在是無語,因為它沒有指明所有可以放在switch中比較的類型,文章開頭提出的問題依然沒有答案。
我們不妨動手試一下,用switch匹配對象:
class A { } var o = A() var o1 = A() var o2 = A() switch o { case o1: print("it is o1") case o2: print("it is o2") default: print("not o1 or o2") }
果然,編譯器報錯了:“Expression pattern of type 'A' cannot match values of type 'A'”。至少我們目前還不明白“expression pattern”是什么,怎么類型A就不能匹配類型A了。
我們做一下改動,在case語句后面加上let:
switch o { case let o1: print("it is o1") case let o2: print("it is o2") default: print("not o1 or o2") }
OK,編譯運行,結(jié)果是:it is o1。這是因為case let不是匹配值,而是值綁定,也就是把o的值賦給臨時變量o1,這在o是可選類型時很有用,類似于if let那樣的隱式解析可選類型。沒有打出it is o2是因為swift中的switch,只匹配第一個相符的case,然后就結(jié)束了,即使不寫break也不會跳到后面的case。
扯遠了,回到話題上來,既然添加let不行,我們得想別的辦法。這時候不妨考慮一下switch語句是怎么實現(xiàn)的。據(jù)我個人猜測,估計類似于用了好多個if判斷有沒有匹配的case,那既然如此,我們給類型A重載一下==運算符試試:
class A {} func == (lhs: A, rhs: A) -> Bool { return true } var o = A(); var o1 = A() ;var o2 = A() switch o { case o1: print("it is o1") case o2: print("it is o2") default: print("not o1 or o2") }
很顯然,又失敗了。如果這就能搞定問題,那這篇文章也太水了。報錯信息和之前一樣??蓡栴}是我們已經(jīng)重載了==運算符,為什么A類型還是不能餓匹配A類型呢,難道switch不用判斷兩個變量是否相等么。
switch作為一個多條件匹配的語句,自然是要判斷變量是否相等的,不過它不是通過==運算符判斷,而是通過~=運算符。再來看一段官方文檔的解釋:
An expression pattern represents the value of an expression. Expression patterns appear only in switch statement case labels.
以及這句話:
The expression represented by the expression pattern is compared with the value of an input expression using the Swift standard library ~= operator.
第一句解釋了之前的報錯,所謂的“express pattern”是指表達式的值,這個概念只在switch的case標簽中有。所以之前的報錯信息是說:“o1這個表達式的值(還是o1)與傳入的參數(shù)o都是類型A的,但它們無法匹配”。至于為什么不能匹配,答案在第二句話中,因為o1和o的匹配是通過調(diào)用標準庫中的~=運算符完成的。
所以,只要把重載==換成重載~=就可以了。改動一個字符,別的都不用改,然后程序就可以運行了。Swift默認在~=運算符中調(diào)用==運算符,這也就是為什么我們感覺不到匹配整數(shù)類型需要什么額外處理。但對于自定義類型來說,不重載~=運算符,就算你重載了==也是沒用的。
除此以外,還有一種解決方法,那就是讓A類型實現(xiàn)Equatable協(xié)議。這樣就不需要重載~=運算符了。答案就在Swift的module的最后幾行:
@warn_unused_result public func ~=<T : Equatable>(a: T, b: T) -> Bool
Swift已經(jīng)為所有實現(xiàn)了Equatable協(xié)議的類重載了~=運算符。雖然實現(xiàn)Equatable協(xié)議只要求重載==運算符,但如果你不顯式的注明遵守了Equatable協(xié)議,swift是無法知道的。因此,如果你重載了==運算符,就順手標注一下實現(xiàn)了Equatable協(xié)議吧,這樣還有很多好處,比如SequenceType的split方法等。
最后總結(jié)一句:
能放在switch語句中的類型必須重載~=運算符,或者實現(xiàn)Equatable協(xié)議。
相關(guān)文章
Swift實現(xiàn)Selection Sort選擇排序算法的實例講解
選擇排序是一種穩(wěn)定的排序算法,且實現(xiàn)代碼通常比冒泡排序要來的簡單,這里我們就來看一下Swift實現(xiàn)Selection Sort選擇排序的實例講解2016-07-07swift3.0 創(chuàng)建sqlite數(shù)據(jù)庫步驟方法
本篇文章主要介紹了swift3.0 創(chuàng)建sqlite數(shù)據(jù)庫步驟方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06