先說IEnumerable,我們每天用的foreach你真的懂它嗎?
我們先思考幾個(gè)問題:
- 為什么在foreach中不能修改item的值?
- 要實(shí)現(xiàn)foreach需要滿足什么條件?
- 為什么Linq to Object中要返回IEnumerable?
接下來,先開始我們的正文。
自己實(shí)現(xiàn)迭代器
.net中迭代器是通過IEnumerable和IEnumerator接口來實(shí)現(xiàn)的,今天我們也來依葫蘆畫瓢。
首先來看看這兩個(gè)接口的定義:


并沒有想象的那么復(fù)雜。其中IEnumerable只有一個(gè)返回IEnumerator的GetEnumerator方法。而IEnumerator中有兩個(gè)方法加一個(gè)屬性。
接下來開發(fā)畫瓢,我們繼承IEnumerable接口并實(shí)現(xiàn):

下面使用原始的方式調(diào)用:

有朋友開始說了,我們平時(shí)都是通過foreache來取值的,沒有這樣使用過啊。好吧,我們來使用foreach循環(huán):

為什么說基本上是等效的呢?我們先看打印結(jié)果,在看反編譯代碼。


由此可見,兩者有這么個(gè)關(guān)系:

我們可以回答第一個(gè)問題了“為什么在foreach中不能修改item的值?”:

我們還記得IEnumerator的定義嗎

接口的定義就只有g(shù)et沒有set。所以我們在foreach中不能修改item的值。
我們再來回答第二個(gè)問題:“要實(shí)現(xiàn)foreach需要滿足什么條件?”:
必須實(shí)現(xiàn)IEnumerable接口?NO

我們自己寫的MyIEnumerable刪掉后面的IEnumerable接口一樣可以foreach(不信?自己去測試)。
所以要可以foreach只需要對象定義了GetEnumerator無參方法,并且返回值是IEnumerator或其對應(yīng)的泛型。細(xì)看下圖:

也就是說,只要可以滿足這三步調(diào)用即可。不一定要繼承于IEnumerable。有意思吧!下次面試官問你的時(shí)候一定要爭個(gè)死去活來啊,哈哈!
yield的使用
你肯定發(fā)現(xiàn)了我們自己去實(shí)現(xiàn)IEnumerator接口還是有些許麻煩,并且上面的代碼肯定是不夠健壯。對的,.net給我們提供了更好的方式。

你會(huì)發(fā)現(xiàn)我們連MyIEnumerator都沒要了,也可以正常運(yùn)行。太神奇了。yield到底為我們做了什么呢?

好家伙,我們之前寫的那一大坨。你一個(gè)yield關(guān)鍵字就搞定了。最妙的是這塊代碼:

這就是所謂的狀態(tài)機(jī)吧!
我們繼續(xù)來看GetEnumerator的定義和調(diào)用:

我們調(diào)用GetEnumerator的時(shí)候,看似里面for循環(huán)了一次,其實(shí)這個(gè)時(shí)候沒有做任何操作。只有調(diào)用MoveNext的時(shí)候才會(huì)對應(yīng)調(diào)用for循環(huán):

現(xiàn)在我想可以回答你“為什么Linq to Object中要返回IEnumerable?”:
因?yàn)镮Enumerable是延遲加載的,每次訪問的時(shí)候才取值。也就是我們在Lambda里面寫的where、select并沒有循環(huán)遍歷(只是在組裝條件),只有在ToList或foreache的時(shí)候才真正去集合取值了。這樣大大提高了性能。
如:

這個(gè)時(shí)候得到了就是IEnumerable對象,但是沒有去任何遍歷的操作。(對照上面的gif動(dòng)圖看)
什么,你還是不信?那我們再來做個(gè)實(shí)驗(yàn),自己實(shí)現(xiàn)MyWhere:

現(xiàn)在看到了吧。執(zhí)行到MyWhere的時(shí)候什么動(dòng)作都沒有(返回的就是IEnumerable),只有執(zhí)行到ToList的時(shí)候才代碼才真正的去遍歷篩選。
這里的MyWhere其實(shí)可以用擴(kuò)展方法來實(shí)現(xiàn),提升逼格。(Linq的那些查詢操作符就是以擴(kuò)展的形式實(shí)現(xiàn)的)[了解擴(kuò)展方法]。
怎樣高性能的隨機(jī)取IEnumerable中的值

這段代碼來源《深入理解C#》,個(gè)人覺得非常妙。貼出來給大家欣賞哈。
結(jié)束:
demo下載:http://pan.baidu.com/s/1dE94c1b
接下篇:《再講IQueryable<T>,揭開表達(dá)式樹的神秘面紗》
本文以同步至《C#基礎(chǔ)知識鞏固系列》
- 學(xué)習(xí)本是一個(gè)不斷抄襲、模仿、練習(xí)、創(chuàng)新的過程。
- 雖然,園中已有本人無法超越的同主題博文,為什么還是要寫。
- 對于自己,博文只是總結(jié)。在總結(jié)的過程發(fā)現(xiàn)問題,解決問題。
- 對于他人,在此過程如果還能附帶幫助他人,那就再好不過了。
- 由于博主能力有限,文中可能存在描述不正確,歡迎指正、補(bǔ)充!
- 感謝您的閱讀。如果文章對您有用,那么請輕輕點(diǎn)個(gè)贊,以資鼓勵(lì)。
- 工控物聯(lián)Q群:995475200
我們先思考幾個(gè)問題: 接下來,先開始我們的正文。 自己實(shí)現(xiàn)迭代器 .net中迭代器是通過IEnumerable和IEnumerator接口來實(shí)現(xiàn)的,今天我們也來依葫蘆畫瓢。 首先來看看這兩個(gè)接口的定義: 并沒有想象的那么復(fù)雜。其中IEnumerable只有一個(gè)返回IEnumerator的GetEnu
浙公網(wǎng)安備 33010602011771號