C# 列表模式
1、語法
為了將集合的元素提取出來判斷,C# 擁有了列表模式。
列表模式是將一個(gè)不知道是不是集合的對(duì)象,用列表的格式列舉出來,對(duì)其中的元素挨個(gè)進(jìn)行判斷的模式。
我們使用一對(duì)大括號(hào)進(jìn)行判斷。使用范圍記號(hào) ..
來表達(dá)“這是一個(gè)范圍”。舉個(gè)例子:[1, .., 3]
表示判斷一個(gè)序列的第一個(gè)元素是不是 1,而最后一個(gè)元素是不是 3。所以,自然這個(gè)寫法就等價(jià)于下面這個(gè)格式了:
它等價(jià)于
這里的 ^1
是 C# 8 里的表達(dá)式,表示倒數(shù)第一個(gè)元素。^n
就是倒數(shù)第 n 個(gè)元素。可以從這個(gè)寫法里看出,..
是靈活的:它不是固定長度,是隨著整個(gè)模式匹配的序列來確定 ..
的長度的。這么寫是為了簡化代碼的書寫格式。
當(dāng)然,假設(shè)我們判斷倒數(shù)第二個(gè)元素而不是倒數(shù)第一個(gè)的話,那么我們可以嘗試在倒數(shù)第一個(gè)元素的判斷信息上添加棄元記號(hào) _
來表達(dá)占位:
棄元記號(hào)在這里起到了很重要的作用。一個(gè)棄元記號(hào)占一個(gè)位置,這恰好表達(dá)和判斷了 arr[^2]
的數(shù)據(jù),而不是 arr[^1]
。
2、預(yù)防性長度判斷
和前文一致,要用這個(gè)模式的話,這個(gè)數(shù)據(jù)類型除了擁有 Length
或 Count
屬性外,索引器成員是必不可少的。另外,如果你不寫上范圍記號(hào) ..
的話,就成了判斷恰好這些數(shù)據(jù)了。
這三個(gè)寫法的區(qū)別是,第一個(gè)和第二個(gè)是一樣的判斷:因?yàn)?[1, 2, 4]
按照順序,判斷的都是前三個(gè)數(shù)據(jù)的數(shù)值,因此長度給出后,判斷的自然是前三個(gè)數(shù)據(jù)了,而后續(xù)的數(shù)據(jù)不用管,寫上 ..
和不寫 ..
都是沒有關(guān)系的。但是第三個(gè)則不一樣了。第三個(gè)因?yàn)殚L度模式不存在的關(guān)系,模式匹配的長度模式會(huì)依賴于 [1, 2, 4]
這個(gè)列表模式。這個(gè)模式只給出了三個(gè)元素,因此不寫長度模式的話,編譯器會(huì)認(rèn)為這個(gè)寫法下,長度模式是 { Length: 3 }
;相反,如果你加上了 ..
的話,編譯器就不再去確認(rèn)后面的元素信息了。
但是,為了避免拋出異常,C# 會(huì)貼心地做一個(gè)“預(yù)防性判斷”。如果 arr
沒有這么長呢?假設(shè) arr
就倆元素,那么判斷 [1, 2, 4, .. ]
就可能產(chǎn)生一個(gè)異常。因此,C# 會(huì)自動(dòng)生成一條預(yù)判長度語句:arr.Length >= 3
。
因此,如下四種寫法的等價(jià)格式是這樣的:

“默認(rèn)生成預(yù)防性長度判斷”這一點(diǎn)比較隱晦,因此你一定要記住。
3、注意使用條件
在列表模式里,我們約定數(shù)據(jù)類型必須包含索引器和 Count
或 Length
屬性才可以使用列表模式,下面有兩個(gè)最容易忽略也會(huì)被當(dāng)成可以用列表模式,實(shí)際上不然的數(shù)據(jù)類型。
Dictionary<TKey, TValue>
類型;ICollection<T>
類型。
首先,字典數(shù)據(jù)類型的索引器并不是 int
數(shù)據(jù)類型,而是 TKey
這個(gè)泛型數(shù)據(jù)類型的。由于 C# 對(duì)列表模式的語法設(shè)計(jì)規(guī)則,所以字典無法表達(dá)出合適的寫法,所以字典類型不支持列表模式;同時(shí),ICollection<T>
類型也不支持。原因可能讓人大跌眼鏡:這個(gè)接口壓根就沒有索引器的成員??赡苓@一點(diǎn)非常容易忽略掉,但是也很好想到為什么:實(shí)現(xiàn)了 ICollection<T>
接口的數(shù)據(jù)類型我們都可以叫它們“集合”。但集合只表示和表達(dá)一種合適的存儲(chǔ)序列。但序列不一定是連續(xù)的,也就是說,你不一定可以使用這個(gè)集合類型從前一個(gè)元素來找后一個(gè)元素。最常見的情況就是“不重復(fù)集合”。如果你寫了一個(gè)不重復(fù)集合的話,它可能會(huì)使用哈希碼來建立列表。那么元素之間就不一定是連續(xù)的了,因此你無法定義一種良好的索引機(jī)制獲取每一個(gè)元素。這樣的數(shù)據(jù)類型就不存在索引器一說。正是因?yàn)榇嬖谶@樣的集合數(shù)據(jù)類型不含有索引器,因此你無法對(duì)一個(gè)這樣的數(shù)據(jù)類型使用列表模式,因?yàn)榱斜砟J揭蕾囁饕鳈C(jī)制。
4、無條件成立的列表模式
當(dāng)然,既然可以允許判斷列表模式,那么自然就有 [..]
這種寫法。這個(gè)寫法的意思是,集合的元素?zé)o條件成立。但是,這個(gè)寫法還不如不寫,對(duì)吧。
5、請(qǐng)一定注意,列表模式不是遞歸模式的一部分
如題,列表模式是一個(gè)單獨(dú)的模式,你必須聲明得和別的模式串聯(lián)起來用 and
或 or
連接,所以如下的語法是錯(cuò)的:
int[]
和 [1, _, .., 3]
之間插入一個(gè) and
這一點(diǎn)一定要記住,因?yàn)樗菍?duì)集合作判斷,但大多數(shù)類型也都不是集合,因此不要想著把它和別的模式放在一起;但是,列表模式允許定義內(nèi)聯(lián)變量。
假設(shè)此時(shí)的 expr
是一個(gè)表達(dá)式,我們可以使用此語法定義表達(dá)結(jié)果,并視 result
為表達(dá)式的運(yùn)算結(jié)果。
還有一個(gè)原因是,假設(shè)我們想要確定是否一個(gè)數(shù)據(jù)類型是 int[]
并且沒有元素的話,它的語法是 is int[] and []
。如果我們?nèi)サ?and
使之允許,這個(gè)語法就成了 is int[] []
,這會(huì)被編譯器視為 is int[][]
,即判斷對(duì)象是不是一個(gè)元素是 int
的鋸齒數(shù)組。因此,不加 and
還真不行。