五月天青色头像情侣网名,国产亚洲av片在线观看18女人,黑人巨茎大战俄罗斯美女,扒下她的小内裤打屁股

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

第 110 講:C# 3 之查詢表達(dá)式(十四):group-by 的底層原理

2022-05-16 22:38 作者:SunnieShine  | 我要投稿

今天我們要講的是倒數(shù)第二個(gè)關(guān)鍵字用法 group-by 從句的用法,以及 group-by-into 的用法,它們對(duì)應(yīng)的底層原理。

說(shuō)了好半天了,大家都應(yīng)該看得出來(lái),實(shí)際上關(guān)鍵字全都是被翻譯成一個(gè)一個(gè)的方法調(diào)用,僅此而已。也不是特別復(fù)雜,只是建議寫關(guān)鍵字是因?yàn)楹糜煤脤W(xué)。不過(guò),group-by 這個(gè)就不是那么好用了,畢竟?fàn)砍兜椒纸M的理論就不是那么好理解了。

Part 1 IGrouping 接口和 GroupBy 方法

非常高興我們?cè)僖淮魏瓦@個(gè)接口類型見面了。這個(gè)接口類型在早期我們簡(jiǎn)單說(shuō)過(guò),它就是半個(gè)字典類型。它不帶有完整的鍵值對(duì)查詢的過(guò)程,但它至少包含了一對(duì)多的關(guān)系,一對(duì)多的“一”是 Key 屬性,表示當(dāng)前分組下的鍵;而一對(duì)多的“多”對(duì)應(yīng)的就是集合里的所有元素了。我們還說(shuō)過(guò),它使用起來(lái)就是一次 foreach 循環(huán);而直接使用 Key 屬性就可以獲取分組的分組依據(jù)的數(shù)值(也就是鍵)。

是的,掌握這些就足矣了。下面我們來(lái)說(shuō)說(shuō) GroupBy 方法組。

實(shí)際上,group-by(包括后面說(shuō)的 group-by-into)都被直接翻譯成了 GroupBy 的方法調(diào)用。這個(gè)方法略顯復(fù)雜。先舉個(gè)例子吧,為了用這個(gè)方法。

假設(shè)我想給一個(gè)學(xué)生列表(比如包含我、炭治郎和禰豆子等等的學(xué)生)按照學(xué)生的性別分組:

我們可以看到這里的 selection 變量用到了 group-by 從句。

它等于什么呢?它等于這么寫:

實(shí)際上可以從代碼發(fā)現(xiàn),簡(jiǎn)化挺多的。首先我們直接就把 by 后面的依據(jù)改成了 lambda 的形式,而 lambda 里的參數(shù)名稱,就是 groupby 關(guān)鍵字中間夾的這個(gè)名字。

很好理解。因?yàn)槲覀兎纸M只需要給出分組依據(jù)即可;而分組依據(jù)要想在運(yùn)行期間執(zhí)行和得到運(yùn)用的話,直接傳入一個(gè)實(shí)際的 student.IsBoy 是不合適的,因?yàn)榇蠹叶贾溃粋€(gè)變量就代表的是一個(gè)值,因此這么寫實(shí)際上是直接把 IsBoy 的結(jié)果給直接傳過(guò)去了,這肯定是不合理的。在上一講的內(nèi)容里我們說(shuō)過(guò),要想暫時(shí)存儲(chǔ)一下操作,我們用到的思路是使用委托類型的實(shí)例來(lái)完成。因?yàn)槲芯涂梢赃_(dá)到一種后期來(lái)做的工作,剛好我們這里的分組依據(jù)和排序依據(jù)是類似的,就是需要這樣的處理,才能被后續(xù)調(diào)用。

那么,這樣就算理解了,對(duì)吧。下面我們來(lái)看看稍微復(fù)雜一些的,別的 GroupBy 的重載。

Part 2 GroupBy 方法組

實(shí)際上,GroupBy 方法包含 8 個(gè)重載。這就是為啥這玩意兒復(fù)雜的原因。

  • 集合.GroupBy(分組依據(jù), 元素依據(jù), 映射依據(jù))

  • 集合.GroupBy(分組依據(jù), 元素依據(jù), 映射依據(jù), 相等比較器)

  • 集合.GroupBy(分組依據(jù), 元素依據(jù))

  • 集合.GroupBy(分組依據(jù), 元素依據(jù), 相等比較器)

  • 集合.GroupBy(分組依據(jù), 映射依據(jù))

  • 集合.GroupBy(分組依據(jù), 映射依據(jù), 相等比較器)

  • 集合.GroupBy(分組依據(jù))(上一部分用到的方法)

  • 集合.GroupBy(分組依據(jù), 相等比較器)

我們簡(jiǎn)單說(shuō)說(shuō)吧。帶有相等比較器的那四個(gè)其實(shí)不必多說(shuō)了,在前文的很多內(nèi)容已經(jīng)說(shuō)過(guò)不少了,就是用來(lái)對(duì)于任何稍微復(fù)雜一些的數(shù)據(jù)類型進(jìn)行自定義相等判斷的時(shí)候會(huì)用到的對(duì)象。

我們重點(diǎn)是說(shuō)說(shuō)剩下的四個(gè)不帶相等比較器的方法。

2-1 利用一對(duì)多改變迭代結(jié)果:GroupBy(分組依據(jù), 映射依據(jù))

先來(lái)說(shuō)這個(gè)。這個(gè)方法需要傳入兩個(gè) lambda,其中第一個(gè) lambda 和前文描述的是一樣的,就是分組依據(jù);而第二個(gè) lambda 是多出來(lái)的參數(shù)。這個(gè) lambda 我們要求的是傳入兩個(gè)參數(shù),第一個(gè)參數(shù)是分組依據(jù)的那個(gè)值(你可以簡(jiǎn)單理解成分組出來(lái)的那個(gè)鍵),而第二個(gè)參數(shù)則是該分組的所有元素。

假設(shè)我們?nèi)匀贿€是按照前文給的按學(xué)生性別分組,那么得到的序列顯然是兩個(gè)“一對(duì)多”的列表。啥?為啥是兩個(gè)?因?yàn)橐粋€(gè)分組是針對(duì)于男生的序列,另外一個(gè)是針對(duì)于女生的序列,所以是兩個(gè)咯。為啥又是一對(duì)多呢?因?yàn)檫@兩個(gè)序列里,同一個(gè)序列里的所有對(duì)象的性別是相同的,比如男生的都在前面這一組,女生則都在后面這一組。那么男生和女生作為分組的依據(jù)的話,那自然就是一對(duì)多的關(guān)系咯。

那么,有些時(shí)候我們可能會(huì)將這樣的一對(duì)多的序列在方法里就完成迭代,于是我們可能會(huì)這么用方法:

請(qǐng)看這個(gè)例子。這個(gè)例子的 GroupBy 傳入了額外的一個(gè)參數(shù),是一個(gè)帶兩個(gè)參數(shù)的 lambda。這兩個(gè)參數(shù)就表示的是分組后的結(jié)果的這個(gè)一對(duì)多的“一”和一對(duì)多的“多”。

我們?cè)谶@個(gè) lambda 里寫了復(fù)雜的執(zhí)行過(guò)程:首先先得到 isBoy 這個(gè)布爾結(jié)果,如果是 true 就說(shuō)明是男生,那么 genderString 就是 Boys: 這個(gè)字符串;而如果是 false 就說(shuō)明是女生,那么 genderString 就是 Girls: 這個(gè)字符串。

然后,我們用到一個(gè) string.Join 方法,里面的第二個(gè)參數(shù)則是獲取整個(gè)序列里所有學(xué)生的名字屬性,然后整合起來(lái),作為字符串拼接的對(duì)象。string.Join 大家再熟悉不過(guò)了,就是拼接一個(gè)序列里的所有成員的字符串寫法,然后指定一個(gè)參數(shù)表示拼接的時(shí)候用什么字符串作為分隔。

最后,我們使用 string.Format 拼接前面給的 genderString 變量和 studentNames 變量。至此返回這個(gè)字符串結(jié)果。

這樣,我們就完成了這個(gè)方法的使用;這個(gè)方法返回的結(jié)果是什么類型的呢?剛才不是我們傳入了第二個(gè)參數(shù)嗎?第二個(gè)參數(shù)這個(gè) lambda 不是返回一個(gè)字符串嗎?所以這個(gè) selection 執(zhí)行后,得到的序列其實(shí)是 IEnumerable<string> 類型的。

那么,我們從第 11 行開始遍歷迭代這個(gè) selection 變量,得到每一個(gè)字符串,直接輸出。我們看看能得到什么結(jié)果:

是的,就是我們?cè)诘诙€(gè) lambda 里返回的每一個(gè)元素的結(jié)果,被我們最后通過(guò) foreach 循環(huán)給迭代出來(lái)了。這就是這個(gè)方法的用法。還行,對(duì)吧。

2-2 改變一對(duì)多的多:GroupBy(分組依據(jù), 元素依據(jù))

這個(gè)方法看起來(lái)復(fù)雜,但實(shí)際上比前面那個(gè)還簡(jiǎn)單。這個(gè)方法也需要兩個(gè)參數(shù),而且第二個(gè)參數(shù)也是 lambda,不過(guò)這個(gè)參數(shù)則表示的是怎么去將一對(duì)多的多給體現(xiàn)出來(lái)。

我們前文不是搞了一個(gè)按照男女的分組嗎,現(xiàn)在我們只需要顯示學(xué)生的名字,那么我們就只需要提取出它的名字而已,而不需要完整地把 Student 類型的實(shí)例當(dāng)結(jié)果給迭代出來(lái)。于是,我們可以這么做:

這個(gè)例子,我們可以看到,我們額外帶了一個(gè) lambda。不過(guò)這個(gè) lambda 只需要傳入一個(gè)參數(shù)即可。這個(gè)參數(shù)其實(shí)就是我們映射的元素。

在我們多給出了這個(gè) lambda 之后,該方法的返回值也會(huì)跟著變:它會(huì)變成 IEnumerable<IGrouping<bool, string>> 類型。我們之前說(shuō)過(guò),長(zhǎng)泛型名稱的理解辦法是從內(nèi)到外。先把最外側(cè)的這個(gè) IEnumerable<> 拿掉,我們可以得到 IGrouping<bool, string>。這其實(shí)就是指的一個(gè)一對(duì)多關(guān)系,一對(duì)多的“一”是 bool 類型,而一對(duì)多的“多”則是 string 類型。為啥是 string 了呢?因?yàn)槲覀儎偛诺诙€(gè)額外的 lambda 規(guī)定了映射的結(jié)果實(shí)際上不再是 Student 實(shí)例了,而是一個(gè)字符串。

由于我們指定了字符串當(dāng)結(jié)果,因此一對(duì)多之后的多就是這個(gè)字符串了。因此我們直接可以通過(guò)迭代集合,Key 屬性就不多說(shuō)了,而內(nèi)部遍歷這個(gè)一對(duì)多列表,就可以得到每一個(gè)元素都是 string 的實(shí)例了。

還是看下結(jié)果吧:

確實(shí)是合理的。那么這個(gè)方法我們也說(shuō)完了。

2-3 融合到一起:GroupBy(分組依據(jù), 元素依據(jù), 映射依據(jù))

如果我們?cè)囍亚懊孢@兩個(gè) rua 到一起呢?是的,GroupBy 也可以做到。不過(guò)這次我們需要傳入三個(gè) lambda 當(dāng)參數(shù)了。注意,順序不要寫錯(cuò)了:顯示分組依據(jù),然后是傳一個(gè)參數(shù)的 lambda,最后才是傳兩個(gè)參數(shù)的 lambda。

好理解嗎?第一個(gè) lambda 是分組依據(jù),就不多說(shuō)了;第二個(gè)參數(shù)是元素依據(jù),到底每一個(gè)元素應(yīng)該是什么結(jié)果。此時(shí)我們將分組后的一對(duì)多序列的多改成字符串,讀取每個(gè)學(xué)生的名字就可以了;第三個(gè)參數(shù)則是這個(gè)最復(fù)雜的 lambda 了。這一次,因?yàn)槲覀兊诙€(gè)參數(shù)規(guī)定了一對(duì)多的多是什么樣的,因此第三個(gè) lambda 里的第二個(gè)參數(shù)的參數(shù)名再叫 students 就略微不太合理了。因?yàn)槲覀円粚?duì)多的多是學(xué)生名,所以這里的參數(shù)名我們改成了 studentNames。

然后,我們直接放在 string.Join 里當(dāng)參數(shù),方便至極。

最后,我們照樣迭代序列,這個(gè)不用多改動(dòng)。得到的結(jié)果應(yīng)該是和前面的結(jié)果是一樣的。只不過(guò)這么寫就把操作分成三個(gè) lambda 了。思路更清晰一些,雖然也更復(fù)雜一些。

Part 3 到頭來(lái)好像還是沒說(shuō) into 從句的等價(jià)代換啊……

哪有那么復(fù)雜呢……還記得嗎,GroupBy 方法的初始返回結(jié)果是啥類型的?序列,對(duì)吧。into 后跟的啥類型的變量?一對(duì)多的這個(gè)結(jié)果序列,對(duì)吧。那是不是就是在 GroupBy 后面加一個(gè) Select 方法調(diào)用就可以了呢?

是的。

比如我有這么一個(gè)方法。那它是啥意思呢?

對(duì)吧。就是這么簡(jiǎn)單。

那么,本來(lái)按慣例是想給大家呈現(xiàn)一下映射的關(guān)系圖的,但是太復(fù)雜了,我今天就先鴿了,改日有空更新文檔的時(shí)候來(lái)加吧。


第 110 講:C# 3 之查詢表達(dá)式(十四):group-by 的底層原理的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
略阳县| 海伦市| 东乌| 鄱阳县| 竹山县| 河津市| 东乌珠穆沁旗| 平武县| 木兰县| 湟中县| 嵊州市| 仪陇县| 六枝特区| 敦化市| 贵阳市| 广宗县| 泗洪县| 淮南市| 韶关市| 漳浦县| 光泽县| 泰和县| 连山| 定州市| 霍城县| 南皮县| 盖州市| 泗阳县| 乃东县| 萨嘎县| 新昌县| 平果县| 南江县| 卓资县| 随州市| 喀喇| 白银市| 西昌市| 乐业县| 铜川市| 南江县|