天天看點

先說IEnumerable,我們每天用的foreach你真的懂它嗎?

先說IEnumerable,我們每天用的foreach你真的懂它嗎?

我們先思考幾個問題: 接下來,先開始我們的正文。 自己實作疊代器 .net中疊代器是通過IEnumerable和IEnumerator接口來實作的,今天我們也來依葫蘆畫瓢。 首先來看看這兩個接口的定義: 并沒有想象的那麼複雜。其中IEnumerable隻有一個傳回IEnumerator的GetEnu

我們先思考幾個問題:

為什麼在foreach中不能修改item的值?

要實作foreach需要滿足什麼條件?

為什麼Linq to Object中要傳回IEnumerable?

接下來,先開始我們的正文。

.net中疊代器是通過IEnumerable和IEnumerator接口來實作的,今天我們也來依葫蘆畫瓢。

首先來看看這兩個接口的定義:

先說IEnumerable,我們每天用的foreach你真的懂它嗎?
先說IEnumerable,我們每天用的foreach你真的懂它嗎?

并沒有想象的那麼複雜。其中IEnumerable隻有一個傳回IEnumerator的GetEnumerator方法。而IEnumerator中有兩個方法加一個屬性。

接下來開發畫瓢,我們繼承IEnumerable接口并實作:

先說IEnumerable,我們每天用的foreach你真的懂它嗎?

下面使用原始的方式調用:

先說IEnumerable,我們每天用的foreach你真的懂它嗎?

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

先說IEnumerable,我們每天用的foreach你真的懂它嗎?

為什麼說基本上是等效的呢?我們先看列印結果,在看反編譯代碼。

先說IEnumerable,我們每天用的foreach你真的懂它嗎?
先說IEnumerable,我們每天用的foreach你真的懂它嗎?

由此可見,兩者有這麼個關系:

先說IEnumerable,我們每天用的foreach你真的懂它嗎?

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

先說IEnumerable,我們每天用的foreach你真的懂它嗎?

我們還記得IEnumerator的定義嗎

先說IEnumerable,我們每天用的foreach你真的懂它嗎?

接口的定義就隻有get沒有set。是以我們在foreach中不能修改item的值。

我們再來回答第二個問題:“要實作foreach需要滿足什麼條件?”:

必須實作IEnumerable接口?NO

先說IEnumerable,我們每天用的foreach你真的懂它嗎?

我們自己寫的MyIEnumerable删掉後面的IEnumerable接口一樣可以foreach(不信?自己去測試)。

是以要可以foreach隻需要對象定義了GetEnumerator無參方法,并且傳回值是IEnumerator或其對應的泛型。細看下圖:

先說IEnumerable,我們每天用的foreach你真的懂它嗎?

也就是說,隻要可以滿足這三步調用即可。不一定要繼承于IEnumerable。有意思吧!下次面試官問你的時候一定要争個死去活來啊,哈哈!

你肯定發現了我們自己去實作IEnumerator接口還是有些許麻煩,并且上面的代碼肯定是不夠健壯。對的,.net給我們提供了更好的方式。

先說IEnumerable,我們每天用的foreach你真的懂它嗎?

你會發現我們連MyIEnumerator都沒要了,也可以正常運作。太神奇了。yield到底為我們做了什麼呢?

先說IEnumerable,我們每天用的foreach你真的懂它嗎?

好家夥,我們之前寫的那一大坨。你一個yield關鍵字就搞定了。最妙的是這塊代碼:

先說IEnumerable,我們每天用的foreach你真的懂它嗎?

這就是所謂的狀态機吧!

我們繼續來看GetEnumerator的定義和調用:

先說IEnumerable,我們每天用的foreach你真的懂它嗎?

我們調用GetEnumerator的時候,看似裡面for循環了一次,其實這個時候沒有做任何操作。隻有調用MoveNext的時候才會對應調用for循環:

先說IEnumerable,我們每天用的foreach你真的懂它嗎?

現在我想可以回答你“為什麼Linq to Object中要傳回IEnumerable?”:

因為IEnumerable是延遲加載的,每次通路的時候才取值。也就是我們在Lambda裡面寫的where、select并沒有循環周遊(隻是在組裝條件),隻有在ToList或foreache的時候才真正去集合取值了。這樣大大提高了性能。

如:

先說IEnumerable,我們每天用的foreach你真的懂它嗎?

這個時候得到了就是IEnumerable對象,但是沒有去任何周遊的操作。(對照上面的gif動圖看)

什麼,你還是不信?那我們再來做個實驗,自己實作MyWhere:

先說IEnumerable,我們每天用的foreach你真的懂它嗎?

現在看到了吧。執行到MyWhere的時候什麼動作都沒有(傳回的就是IEnumerable),隻有執行到ToList的時候才代碼才真正的去周遊篩選。

這裡的MyWhere其實可以用擴充方法來實作,提升逼格。(Linq的那些查詢操作符就是以擴充的形式實作的)[了解擴充方法]。

先說IEnumerable,我們每天用的foreach你真的懂它嗎?

這段代碼來源《深入了解C#》,個人覺得非常妙。貼出來給大家欣賞哈。

結束:

demo下載下傳:http://pan.baidu.com/s/1dE94c1b

接下篇:《再講IQueryable<T>,揭開表達式樹的神秘面紗》

本文以同步至《C#基礎知識鞏固系列》

學習本是一個不斷抄襲、模仿、練習、創新的過程。

雖然,園中已有本人無法超越的同主題博文,為什麼還是要寫。

對于自己,博文隻是總結。在總結的過程發現問題,解決問題。

對于他人,在此過程如果還能附帶幫助他人,那就再好不過了。

由于部落客能力有限,文中可能存在描述不正确,歡迎指正、補充!

感謝您的閱讀。如果文章對您有用,那麼請輕輕點個贊,以資鼓勵。

工控物聯Q群:995475200

繼續閱讀