Linq專題之提高編碼效率—— 第三篇 你需要知道的枚舉類

来源:http://www.cnblogs.com/huangxincheng/archive/2016/03/14/5275811.html
-Advertisement-
Play Games

   眾所周知,如果一個類可以被枚舉,那麼這個類必須要實現IEnumerable介面,而恰恰我們所有的linq都是一個繼承自IEnumerable介面的匿名類, 那麼問題就來了,IEnumerable使了何等神通讓這些集合類型可以被自由的枚舉???   一: 探索IEnumerable 首先我們看看


 

   眾所周知,如果一個類可以被枚舉,那麼這個類必須要實現IEnumerable介面,而恰恰我們所有的linq都是一個繼承自IEnumerable介面的匿名類,

那麼問題就來了,IEnumerable使了何等神通讓這些集合類型可以被自由的枚舉???

 

一: 探索IEnumerable

  首先我們看看此介面都定義了些什麼東西,如ILSpy所示:

 

從這個介面中,好像也僅僅有一個IEnumerator介面類型的方法之外,並沒有可以挖掘的東西,這時候大家就應該好奇了,foreach既然可以枚舉Collection,

那foreach背後的機制和GetEnumerator()有什麼關係呢???說乾就乾,我們寫一個demo,用ILDasm看看背後的IL應該就清楚了。

 

C#代碼:

     static void Main(string[] args)
        {
            List<Action> list = new List<Action>();

            foreach (var item in list)
            {
                Console.WriteLine();
            }
        }

 

IL代碼:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       60 (0x3c)
  .maxstack  1
  .locals init ([0] class [mscorlib]System.Collections.Generic.List`1<class [mscorlib]System.Action> list,
           [1] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<class [mscorlib]System.Action> V_1,
           [2] class [mscorlib]System.Action item)
  IL_0000:  nop
  IL_0001:  newobj     instance void class [mscorlib]System.Collections.Generic.List`1<class [mscorlib]System.Action>::.ctor()
  IL_0006:  stloc.0
  IL_0007:  nop
  IL_0008:  ldloc.0
  IL_0009:  callvirt   instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<class [mscorlib]System.Action>::GetEnumerator()
  IL_000e:  stloc.1
  .try
  {
    IL_000f:  br.s       IL_0021
    IL_0011:  ldloca.s   V_1
    IL_0013:  call       instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<class [mscorlib]System.Action>::get_Current()
    IL_0018:  stloc.2
    IL_0019:  nop
    IL_001a:  call       void [mscorlib]System.Console::WriteLine()
    IL_001f:  nop
    IL_0020:  nop
    IL_0021:  ldloca.s   V_1
    IL_0023:  call       instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<class [mscorlib]System.Action>::MoveNext()
    IL_0028:  brtrue.s   IL_0011
    IL_002a:  leave.s    IL_003b
  }  // end .try
  finally
  {
    IL_002c:  ldloca.s   V_1
    IL_002e:  constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<class [mscorlib]System.Action>
    IL_0034:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
    IL_0039:  nop
    IL_003a:  endfinally
  }  // end handler
  IL_003b:  ret
} // end of method Program::Main

 

從IL中標紅的字體來看,原來所謂的foreach,本質上調用的是list的GetEnumerator()方法來返回一個Enumerator枚舉類型,然後在while迴圈中通過

current獲取當前值,然後用MoveNext()獲取下一個值,以此類推,如果把IL還原一下,大概就是下麵這樣:

            var enumerator = list.GetEnumerator();

            try
            {
                while (enumerator.MoveNext())
                {
                    Console.WriteLine(enumerator.Current);
                }
            }
            finally
            {
                enumerator.Dispose();
            }

 

這個時候你是不是有種強烈的欲望來探索GetEnumerator()到底幹了什麼,以及MoveNext()在其中扮演了什麼角色??? 下麵我們用ILSpy看看List下麵

所謂的Enumerator類型。。。

 

 1     [Serializable]
 2         public struct Enumerator : IEnumerator<T>, IDisposable, IEnumerator
 3         {
 4             private List<T> list;
 5             private int index;
 6             private int version;
 7             private T current;
 8             [__DynamicallyInvokable]
 9             public T Current
10             {
11                 [__DynamicallyInvokable]
12                 get
13                 {
14                     return this.current;
15                 }
16             }
17             [__DynamicallyInvokable]
18             object IEnumerator.Current
19             {
20                 [__DynamicallyInvokable]
21                 get
22                 {
23                     if (this.index == 0 || this.index == this.list._size + 1)
24                     {
25                         ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
26                     }
27                     return this.Current;
28                 }
29             }
30             internal Enumerator(List<T> list)
31             {
32                 this.list = list;
33                 this.index = 0;
34                 this.version = list._version;
35                 this.current = default(T);
36             }
37             [__DynamicallyInvokable]
38             public void Dispose()
39             {
40             }
41             [__DynamicallyInvokable]
42             public bool MoveNext()
43             {
44                 List<T> list = this.list;
45                 if (this.version == list._version && this.index < list._size)
46                 {
47                     this.current = list._items[this.index];
48                     this.index++;
49                     return true;
50                 }
51                 return this.MoveNextRare();
52             }
53             private bool MoveNextRare()
54             {
55                 if (this.version != this.list._version)
56                 {
57                     ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
58                 }
59                 this.index = this.list._size + 1;
60                 this.current = default(T);
61                 return false;
62             }
63             [__DynamicallyInvokable]
64             void IEnumerator.Reset()
65             {
66                 if (this.version != this.list._version)
67                 {
68                     ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
69                 }
70                 this.index = 0;
71                 this.current = default(T);
72             }
73         }

 

通過查看所謂的Enumerator類的定義,尤其是標紅的地方,可能會讓你頓然醒悟,其實所謂的枚舉類,僅僅是一個枚舉集合的包裝類,比如這裡的List,

然後枚舉類通過index++ 這種手段來逐一獲取List中的元素,僅此而已。

 

二:yield關鍵詞

  當大家明白了所謂的枚舉類之後,是不是想到了一個怪異的yield詞法,這個掉毛竟然還可以被枚舉,就比如下麵這樣代碼:

 1 class Program
 2 {
 3     static void Main(string[] args)
 4     {
 5         foreach (var item in Person.Run())
 6         {
 7             Console.WriteLine(item);
 8         }
 9 
10     }
11 }
12 
13 class Person
14 {
15     public static IEnumerable<int> Run()
16     {
17         List<int> list = new List<int>();
18 
19         foreach (var item in list)
20         {
21             yield return item;
22         }
23     }
24 }

 

那究竟yield幹了什麼呢? 而且能夠讓它人可以一探究竟??? 我們用ILDasm看一下。

 

仔細查看上面的代碼,原來所謂的yield會給你生成一個枚舉類,而這個枚舉類和剛纔List中的Enumerator枚舉類又無比的一樣,如果你理解了顯示

的枚舉類Enumerator,我想這個匿名的枚舉類Enumerator應該就非常簡單了。

 

好了,大概就說這麼多了,有了這個基礎,我相信linq中返回的那些匿名枚舉類對你來說應該就沒什麼問題了~~~

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.UITabBarController:和UINavigationController類似,也可以輕鬆管理多個控制器,輕鬆完成空間之間的轉換。 2.UITabBarController的使用: (1)使用步驟:          <1>初始化UITabBarController          
  • http://cc.bingj.com/cache.aspx?q=OpenEmbedded+clfs&d=4706495287069596&mkt=zh-CN&setlang=zh-CN&w=afp8GvZe7KNcRFBKF2kYXd8WwVn9MVkI CLFS stands for Cross
  • 七、bash腳本中的流程式控制制  條件判斷控制  -[]([]中的表達式是否為真)  &&(前邊的結果是true的時候執行後邊的命令)   ||(前邊的結果是false的時候執行後邊的命令)  -if  -case  迴圈控制  -while  -utile  -for  -break  -conti
  • 張超《Linux內核分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000 我的代碼可見https://www.shiyanlou.com/courses/reports/986221 在這裡我們用的是linux-3.18.6版本,以下簡寫
  • Nginx最大特點: 靜態小文件(1M),支持高併發,同時占用系統資源很少。3W併發,10個進程,記憶體150M。 Nginx特點: 1、配置簡單,靈活,輕量。 2、高併發(靜態小文件),靜態幾萬的併發。 3、占用資源少。2W併發 開10個線程服務,記憶體消耗幾百M。 4、功能種類較多(Web,cach
  • 一、作用 文件/etc/apt/sources.list是一個普通可編輯的文本文件,保存了ubuntu軟體更新的源伺服器的地址。和sources.list功能一樣的是/etc/apt/sources.list.d/*.list(*代表一個文件名,只能由字母、數字、下劃線、英文句號組成)。source
  • Application Request Route(文中簡稱為ARR)是一個寄宿於 IIS7(及以後的IIS 版本)的一個基於代理的模塊,它可以通過判斷 Http Headers,Server Variables 以及負載均衡演算法將 HTTP 的請求轉發到不同的處理伺服器之上。ARR依賴URL Re...
  • 1、C#常量數據類型只能是原始數據類型:int、bool、char、double、string等。 2、C#中用訪問修飾符來說明變數的可訪問性,其值可以是:private、protected、internal、protected internal和public。 public:訪問不受限制,在任意地
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...