你需要知道的枚举类

来源:互联网 发布:qq三国修复数据 编辑:程序博客网 时间:2024/06/16 00:03

  众所周知,如果一个类可以被枚举,那么这个类必须要实现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 Current10             {11                 [__DynamicallyInvokable]12                 get13                 {14                     return this.current;15                 }16             }17             [__DynamicallyInvokable]18             object IEnumerator.Current19             {20                 [__DynamicallyInvokable]21                 get22                 {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 Person14 {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看一下。

 

0 0
原创粉丝点击