第 108 講:C# 3 之查詢表達式(十二):where 的底層原理
今天我們來介紹 where
關鍵字。where
關鍵字對于 C# 編譯器又是如何實現(xiàn)的呢?
Part 1 Where
方法組
1-1 Where(迭代變量 => 條件)
實際上,where
從句也對應了一個方法,這個方法名字就和 select
從句對應 Select
方法一樣,名字就換了一下大小寫而已。where
從句對應的是 Where
擴展方法。
Where
這個方法仍然要求我們傳入一個 Lambda,表示處理和篩選條件。不過我們要注意的是,Where
也有重載。
先來說等價的這個。
注意到第 8 行的代碼,我們可以看到 Where
方法傳入了一個 Lambda,執(zhí)行過程是取每一個匿名類型對象的姓,然后看它的首字母是不是大寫的 S。如果是的話就取出來。
這等價于什么呢?是不是:
Where

where

select
上的時候,則不會顯示任何東西,因為這個地方我們只用了一次 Where
方法就可以得到結(jié)果了,因此 select

1-2 Where((迭代變量, 索引) => 條件)
不過,它還有一個方法重載,而傳入的 Lambda 需要兩個參數(shù),其中 Lambda 的第二個參數(shù)表示的是這個對象對應迭代期間是第幾個元素,就相當于一個列表的索引。這一點和 Select
類似,這就不展開說明了。
Part 2 where
從句后 select
從句不是簡單的映射關系
顯然,前面給的例子里,select
從句只是簡單的返回,所以看不出啥轉(zhuǎn)換關系。如果說我的 select
從句不是簡單的返回對象本身的話,那么轉(zhuǎn)換為方法的時候,就不能直接只有一個 Where
了。
考慮一下這個例子。這個例子是得到姓首字母是 S 的角色,然后對 Where
調(diào)用之后接上了一個新的 Select
方法的調(diào)用。這意味著什么呢?這是不是就意味著我把 Where
方法當成分水嶺,Where
方法會篩選得到滿足條件的結(jié)果序列,然后對這個篩選后的結(jié)果執(zhí)行一次 Select
從句?
那么按照這種邏輯來看這個調(diào)用的話,是不是就是先來了一次篩選,然后得到的結(jié)果再來了一次映射?那么它應當?shù)葍r于這樣 3 種寫法:
第一種是 where
后的 select
從句直接跟上映射表達式,第二種是在 where
后跟上原始的 select
語句,表示篩選完成。然后再次對篩選完成的 character
變量來一次映射,最后一種則是嵌套查詢,先將篩選條件滿足的對象得到,然后作為基本的迭代序列,然后去迭代。然后迭代的每一個對象,都執(zhí)行一次映射語句,得到最后的結(jié)果。
實際上,三種寫法沒有任何運行上的區(qū)別,但是,第一種是正統(tǒng)的等價關系,第二個和第三個寫法則是通過我們剛才的理解寫出來的“稍微冗余一些的”代碼。
這樣的話,我們就可以看出,如果 where
后跟的 select
不是純粹地返回迭代變量的話,那么它就會再多出來一次 Select
方法的調(diào)用;否則的話,Where
一句就搞定了。但是,在寫查詢表達式的時候,為了保證語句的完整性,我們?nèi)匀灰獙懗?select
從句的部分。
這就是 where
和 Where
方法的等價轉(zhuǎn)換規(guī)則和邏輯。