程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Linq專題之提高編碼效率—— 第三篇 你需要知道的枚舉類

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

編輯:關於.NET

 

   眾所周知,如果一個類可以被枚舉,那麼這個類必須要實現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類型。。。

 

     [Serializable]
         public struct Enumerator : IEnumerator<T>, IDisposable, IEnumerator
         {
             private List<T> list;
             private int index;
             private int version;
             private T current;
             [__DynamicallyInvokable]
             public T Current
             {
                 [__DynamicallyInvokable]
                 get
                 {
                     return this.current;
                 }
             }
             [__DynamicallyInvokable]
             object IEnumerator.Current
             {
                 [__DynamicallyInvokable]
                 get
                 {
                     if (this.index == 0 || this.index == this.list._size + 1)
                     {
                         ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
                     }
                     return this.Current;
                 }
             }
             internal Enumerator(List<T> list)
             {
                 this.list = list;
                 this.index = 0;
                 this.version = list._version;
                 this.current = default(T);
             }
             [__DynamicallyInvokable]
             public void Dispose()
             {
             }
             [__DynamicallyInvokable]
             public bool MoveNext()
             {
                 List<T> list = this.list;
                 if (this.version == list._version && this.index < list._size)
                 {
                     this.current = list._items[this.index];
                     this.index++;
                     return true;
                 }
                 return this.MoveNextRare();
             }
             private bool MoveNextRare()
             {
                 if (this.version != this.list._version)
                 {
                     ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
                 }
                 this.index = this.list._size + 1;
                 this.current = default(T);
                 return false;
             }
             [__DynamicallyInvokable]
             void IEnumerator.Reset()
             {
                 if (this.version != this.list._version)
                 {
                     ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
                 }
                 this.index = 0;
                 this.current = default(T);
             }
         }

 

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

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

 

二:yield關鍵詞

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

 class Program
 {
     static void Main(string[] args)
     {
         foreach (var item in Person.Run())
         {
             Console.WriteLine(item);
         }
 
     }
 }
 
 class Person
 {
     public static IEnumerable<int> Run()
     {
         List<int> list = new List<int>();
 
         foreach (var item in list)
         {
             yield return item;
         }
     }
 }

 

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

 

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

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

 

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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved