C# 解構(gòu)模式
1、語(yǔ)法
因?yàn)榍拔奈覀儞碛辛私鈽?gòu)函數(shù),也擁有了 var
模式,因此 C# 靈活的語(yǔ)法提供了 var
模式的解構(gòu)版本:
var (x, y)
。當(dāng)然,你也可以內(nèi)聯(lián) var
(var x, var y)
的語(yǔ)法。
這樣是可以的。
不過嚴(yán)謹(jǐn)一點(diǎn)的話,
var (x, y)
是解構(gòu)模式,而(var x, var y)
是對(duì)位模式。因?yàn)榍罢呤褂?var (x, y)
語(yǔ)法,小括號(hào)里直接定義了變量名,小括號(hào)的外側(cè)則是var
關(guān)鍵字;但(var x, var y)
在小括號(hào)里定義了兩個(gè)變量,都使用了var
關(guān)鍵字,這意味著是對(duì)應(yīng)位置上的數(shù)據(jù)分別定義變量,類似point.X is var x && point.Y is var y
的效果,因此只能說(shuō)是對(duì)位模式。使用解構(gòu)模式可以更清楚、更簡(jiǎn)明地將對(duì)象進(jìn)行解構(gòu),直接賦值到變量上;但它存在一定的弊端,例如解構(gòu)模式下就不能往里判斷數(shù)值了。也就是說(shuō),你在寫成
var (a, b)
的類似語(yǔ)法后,就無(wú)法往a
、b
上使用任何模式匹配的判別語(yǔ)法了。該嵌套模式匹配之語(yǔ)法將在稍后說(shuō)明。
2、可空值類型解構(gòu)模式的別樣意義
在 C# 里,可空值類型一直是一種方便也不方便的數(shù)據(jù)類型。它的聲明和使用都比較方便,但問題就出在它可能是 null
數(shù)值。假設(shè)前文的 Point
我們用的是可空類型的話:
nullable
是否為 null
(除非看取了 nullable
的值才行)。因此,一旦我們對(duì)這個(gè)類型進(jìn)行解構(gòu):
var
模式一樣。它牽扯到數(shù)據(jù)是不是 null
才可解構(gòu)的問題。如果數(shù)據(jù)都是 null
了,我們就無(wú)法解構(gòu)。因此,可空值類型的解構(gòu)模式會(huì)先判斷對(duì)象是不是不為 null
和
nullable.HasValue
是等效的,所以寫nullable.HasValue
也沒問題。
3、主構(gòu)造器的解構(gòu)模式
是的,主構(gòu)造器會(huì)自動(dòng)生成對(duì)應(yīng)的解構(gòu)函數(shù),因此完全可以直接使用解構(gòu)模式。還是使用之前的 Person
類型:
那么,有這樣的語(yǔ)法:
這樣是允許的。但你不能寫 is Person (name, _, isBoy)
,因?yàn)榍懊娴?var
關(guān)鍵字是這個(gè)模式匹配的固定格式,改成了 Person
的話,后面就只能看成對(duì)位模式了。
4、調(diào)用擴(kuò)展方法的解構(gòu)模式
解構(gòu)模式和對(duì)位模式類似,編譯器也支持嗅探解構(gòu)模式對(duì)應(yīng)的擴(kuò)展方法。一般正常的實(shí)現(xiàn)我們可能對(duì)一些數(shù)據(jù)類型無(wú)法實(shí)現(xiàn)解構(gòu)操作,因此我們需要擴(kuò)展方法來(lái)達(dá)到一些行為。比如假設(shè)我要去獲取數(shù)組的前兩個(gè)元素,我們經(jīng)常會(huì)使用 [0]
和 [1]
來(lái)獲取,不過現(xiàn)在我們可以使用解構(gòu)模式來(lái)完成:
T[]
請(qǐng)注意解構(gòu)函數(shù)正常使用的時(shí)候是盡量不出現(xiàn) 0 或 1 個(gè)元素的解構(gòu)模式,不過在這個(gè)時(shí)候也可能會(huì)遇到,因此語(yǔ)法沒有對(duì)此進(jìn)行限制。
5、解構(gòu)和對(duì)位模式不要求判斷元素?cái)?shù)量至少兩個(gè)
這里稍微說(shuō)一個(gè)比較不容易了解到的知識(shí)點(diǎn)。編譯器限制我們定義一個(gè)至少兩個(gè)元素的值元組 ValueTuple
類型,也就是說(shuō),一個(gè)或零個(gè)的值元組類型是不被允許的:
var
而不是 ValueTuple<int>
的話,編譯器會(huì)自動(dòng)消去 (1)
兩側(cè)的小括號(hào),然后直接認(rèn)為它是 1;故意顯式給出類型名是為了告訴你,這兩個(gè)情況都是值元組不被允許的。
不過,雖然解構(gòu)模式和對(duì)位模式長(zhǎng)得都跟值元組的類型聲明模式很像,但對(duì)位模式和解構(gòu)模式允許和支持解構(gòu)函數(shù)可以包含任意多的 out
參數(shù)用于解構(gòu),這也意味著在解構(gòu)模式和對(duì)位模式里,is ()
或 is (1)
是存在的語(yǔ)法。
6、單元素的解構(gòu)模式要手動(dòng)消除二義性
在 C# 里,小括號(hào)如果不需要是會(huì)被編譯器分析出來(lái)的。比如說(shuō) var a = (1 + 3)
,此時(shí)的小括號(hào)沒有必要需要它。在模式匹配里,單元素的解構(gòu)模式也是一種特殊的處理:它會(huì)被視為常量模式,于是,考慮下面的例子,判斷就有些奇怪了:
請(qǐng)看這樣的代碼。你認(rèn)為它是對(duì)的嗎?答案是不對(duì)。編譯器會(huì)首先認(rèn)為 (42)
是常量模式,而 o
變量是 C
類型而不是一個(gè)整數(shù),因此這個(gè)模式會(huì)導(dǎo)致編譯器直接告知“永遠(yuǎn)都不會(huì)匹配成功”的編譯器錯(cuò)誤。
那么,怎么讓它調(diào)用該解構(gòu)函數(shù)來(lái)完成判別呢?答案其實(shí)很簡(jiǎn)單:消除編譯器認(rèn)為是常量模式的二義性即可。比如給 (42)
模式添加參數(shù)名。
7、任意類型的解構(gòu)模式
對(duì)任何數(shù)據(jù)類型(當(dāng)然,指針類型除外)而言,我們都是可以使用解構(gòu)模式的。這一點(diǎn)很神奇。
可,這會(huì)被視為什么判斷規(guī)則呢?不知道你知不知道一個(gè)類型叫 ITuple
?這個(gè)數(shù)據(jù)類型限制了類型具有元組的性質(zhì)。所以,對(duì)任何數(shù)據(jù)類型來(lái)說(shuō)的解構(gòu)模式,實(shí)際上是被編譯器特殊處理和優(yōu)化過,并認(rèn)為是在匹配該類型的數(shù)據(jù)規(guī)則。
比如 o is ()
會(huì)被視為 o is ITuple tuple && tuple.Length == 0
。注意此時(shí) ITuple
里的 Length
屬性表示的是元組的元素?cái)?shù)。當(dāng)然了,如果你這個(gè)類型具有解構(gòu)函數(shù),就不會(huì)走這個(gè)路線去判斷。但是,如果一個(gè)類型既沒有實(shí)現(xiàn)這個(gè) ITuple
接口,又沒有匹配的解構(gòu)函數(shù),就會(huì)產(chǎn)生編譯器錯(cuò)誤。