細說 C# 中的 IEnumerable,你真的懂 foreach 嗎?
我們先思考幾個問題:
- 為什麼在foreach中不能修改item的值?
- 要實現foreach需要滿足什麼條件?
- 為什麼Linq to Object中要返回IEnumerable?
接下來,先開始我們的正文。
自己實現迭代器
.net中迭代器是通過IEnumerable和IEnumerator介面來實現的,今天我們也來依葫蘆畫瓢。
首先來看看這兩個介面的定義:
並沒有想象的那麼複雜。其中IEnumerable只有一個返回IEnumerator的GetEnumerator方法。而IEnumerator中有兩個方法加一個屬性。
接下來開發畫瓢,我們繼承IEnumerable介面並實現:
下面使用原始的方式呼叫:
有朋友開始說了,我們平時都是通過foreache來取值的,沒有這樣使用過啊。好吧,我們來使用foreach迴圈:
為什麼說基本上是等效的呢?我們先看列印結果,在看反編譯程式碼。
由此可見,兩者有這麼個關係:
我們可以回答第一個問題了“為什麼在foreach中不能修改item的值?”:
我們還記得IEnumerator的定義嗎
介面的定義就只有get沒有set。所以我們在foreach中不能修改item的值。
我們再來回答第二個問題:“要實現foreach需要滿足什麼條件?”:
必須實現IEnumerable介面?NO
我們自己寫的MyIEnumerable刪掉後面的IEnumerable介面一樣可以foreach(不信?自己去測試)。
所以要可以foreach只需要物件定義了GetEnumerator無參方法,並且返回值是IEnumerator或其對應的泛型。細看下圖:
也就是說,只要可以滿足這三步呼叫即可。不一定要繼承於IEnumerable。有意思吧!下次面試官問你的時候一定要爭個死去活來啊,哈哈!
yield的使用
你肯定發現了我們自己去實現IEnumerator介面還是有些許麻煩,並且上面的程式碼肯定是不夠健壯。對的,.net給我們提供了更好的方式。
你會發現我們連MyIEnumerator都沒要了,也可以正常執行。太神奇了。yield到底為我們做了什麼呢?
好傢伙,我們之前寫的那一大坨。你一個yield關鍵字就搞定了。最妙的是這塊程式碼:
這就是所謂的狀態機吧!
我們繼續來看GetEnumerator的定義和呼叫:
我們呼叫GetEnumerator的時候,看似裡面for迴圈了一次,其實這個時候沒有做任何操作。只有呼叫MoveNext的時候才會對應呼叫for迴圈:
現在我想可以回答你“為什麼Linq to Object中要返回IEnumerable?”:
因為IEnumerable是延遲載入的,每次訪問的時候才取值。也就是我們在Lambda裡面寫的where、select並沒有迴圈遍歷(只是在組裝條件),只有在ToList或foreache的時候才真正去集合取值了。這樣大大提高了效能。
如:
這個時候得到了就是IEnumerable物件,但是沒有去任何遍歷的操作。(對照上面的gif動圖看)
什麼,你還是不信?那我們再來做個實驗,自己實現MyWhere:
現在看到了吧。執行到MyWhere的時候什麼動作都沒有(返回的就是IEnumerable),只有執行到ToList的時候才程式碼才真正的去遍歷篩選。
這裡的MyWhere其實可以用擴充套件方法來實現,提升逼格。(Linq的那些查詢操作符就是以擴充套件的形式實現的)[瞭解擴充套件方法]。
怎樣高效能的隨機取IEnumerable中的值
這段程式碼來源《深入理解C#》,個人覺得非常妙。貼出來給大家欣賞哈。
結束:
demo下載:http://pan.baidu.com/s/1dE94c1b
相關文章
- 細說 C# 中的 IEnumerable和IEnumerator介面C#
- TCP|你真的懂 HTTP 嗎?TCPHTTP
- 你真的懂函式嗎?函式
- 你真的懂C++嗎?C++
- 你真的懂JavaScript的正則嗎?JavaScript
- 你真的懂Android的TextView嗎?AndroidTextView
- 你真的懂Python命名嗎?Python
- 騰訊面試,你真的懂HTTP嗎?面試HTTP
- 你真的懂 == 和 equals 的區別嗎?
- RabbitMQ的佇列模式你真的懂嗎MQ佇列模式
- 你真的懂HTML嗎-從”最新快閃記憶體”說起HTML記憶體
- 你真的懂JavaScript計時器嗎?JavaScript
- 分頁查詢,你真的懂嗎?
- 你常說遊戲需要公平,又真的懂遊戲平衡性嗎?遊戲
- 你真的懂JavaScript基礎型別嗎JavaScript型別
- 你真的清楚DateTime in C#嗎?C#
- 真的懂Java的String嗎?Java
- Spring 系列(三):你真的懂@RequestMapping嗎?SpringAPP
- 你真的懂Redis的5種基本資料結構嗎?Redis資料結構
- 你瞭解jsp中的c:forEach嗎?JS
- 瞎搞!你真的懂什麼是ERP、中臺和低程式碼嗎?
- 你真的懂synchronized鎖?synchronized
- 【前端詞典】繼承(一) - 原型鏈你真的懂嗎?前端繼承原型
- 你真的懂模組化嗎?教你CommonJS實現JS
- 你真的懂js獲取可視區寬高嗎JS
- 細思極恐 - 你真的會寫 Java 嗎?Java
- 細思極恐-你真的會寫java嗎?Java
- 你真的懂 Java 的記憶體管理和引用型別嗎?Java記憶體型別
- C#你可能不知道的陷阱, IEnumerable介面C#
- 你真的理解T-sql中的NULL嗎?SQLNull
- 你可以終止 forEach 嗎?
- 你真的理解this嗎
- IEnumerable 介面 實現foreach 遍歷 例項
- 你說你懂計算機網路,那這些你都知道嗎計算機網路
- 你真的對 Linux 中的 Inode 瞭解嗎?Linux
- 你真的會用PostGIS中的buffer緩衝嗎?
- stl中的sort函式,你真的瞭解嗎函式
- java web開發這些細節你真的掌握了嗎JavaWeb