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中返回的那些匿名枚举类对你来说应该就没什么问题了~~~

  • 相关阅读:
    电脑麦克风不能用
    MathType中带上下标字符不对其
    Visio显示不完整
    程序员美食-煎豆腐
    Visio画好的图在word中只显示一部分
    Visio中旋转文本框与箭头平行
    Tikhonov regularization和岭回归
    linux下使用sftp
    git cherry-pick. 如何把已经提交的commit, 从一个分支放到另一个分支
    python 操作消息队列
  • 原文地址:https://www.cnblogs.com/huangxincheng/p/5275811.html
Copyright © 2011-2022 走看看