zoukankan      html  css  js  c++  java
  • Linq专题之提高编码效率—— 第三篇 你需要知道的枚举类

       众所周知,如果一个类可以被枚举,那么这个类必须要实现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中返回的那些匿名枚举类对你来说应该就没什么问题了~~~

  • 相关阅读:
    读书笔记之理想设计的特征
    一些javascript 变量声明的 疑惑
    LINQ 使用方法
    Google MySQL tool releases
    读书笔记之设计的层次
    EF之数据库连接问题The specified named connection is either not found in the configuration, not intended to be used with the Ent
    转载 什么是闭包
    javascript面向对象起步
    Tips
    数据结构在游戏中的应用
  • 原文地址:https://www.cnblogs.com/huangxincheng/p/5275811.html
Copyright © 2011-2022 走看看