• IL Discovery 系列 二《 foreach语句的技术内幕》


    今天在阅读《CLR via C#》,书中说

    “使用foreach语句时,会在finally块中调用IEnumerator对象的Dispose方法”(第20章 异常和状态管理,P434)

    自己很奇怪的是,在编译foreach语句时也会生成try/finally语句块吗?会吧,不会吧!!

    于是自己做了如下实验:

            static void ILDiscoveryForeach()
            {
                var someList = Enumerable.Range(0, 10);
                foreach (var item in someList)
                {
    
                }
            }

    使用ILDasm工具得到的IL语言如下:

    .method private hidebysig static void  ILDiscoveryForeach() cil managed
    {
      // Code size       46 (0x2e)
      .maxstack  2
      .locals init ([0] class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> someList,
               [1] class [mscorlib]System.Collections.Generic.IEnumerator`1<int32> CS$5$0000)
      IL_0000:  ldc.i4.0
      IL_0001:  ldc.i4.s   10
      IL_0003:  call       class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> [System.Core]System.Linq.Enumerable::Range(int32,
                                                                                                                                      int32)
      IL_0008:  stloc.0
      IL_0009:  ldloc.0
      IL_000a:  callvirt   instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()   // 得到someList对象的枚举器
      IL_000f:  stloc.1
      .try
      {
        IL_0010:  br.s       IL_0019    // 无条件跳转到19行
        IL_0012:  ldloc.1    // 将枚举器压入堆栈
        IL_0013:  callvirt   instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<int32>::get_Current()    // 调用枚举器的get_Current()方法
        IL_0018:  pop    // 
        IL_0019:  ldloc.1    // 将枚举器压入堆栈 
        IL_001a:  callvirt   instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()    // 调用MoveNext()方法
        IL_001f:  brtrue.s   IL_0012    // 如果返回值为true,则到12行
        IL_0021:  leave.s    IL_002d    // 如果遍历结束,则退出try语句块,到2d行,但之前会执行finally语句块的内容
      }  // end .try
      finally
      {
        // 如果枚举器不为空,则调用枚举器的Dispose()方法!
        IL_0023:  ldloc.1
        IL_0024:  brfalse.s  IL_002c
        IL_0026:  ldloc.1
        IL_0027:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
        IL_002c:  endfinally
      }  // end handler
      IL_002d:  ret
    } // end of method Program::ILDiscoveryForeach
    
    从上面可以知道,Jeffery说的是对的,Dispose不是Enumerable对象,而是Enumerator对象!!
     
    这样一切都很合理了。
     
    但是对于一个数组对象的foreach遍历又是怎样的呢?
    代码:
            static void ILDiscoveryForeach()
            {
                int[] someList = { 1, 2, 3, 4, 5 };
                foreach (var item in someList)
                {
    
                }
            }
    

    IL:

    .method private hidebysig static void  ILDiscoveryForeach() cil managed
    {
      // Code size       39 (0x27)
      .maxstack  3
      .locals init ([0] int32[] someList,
               [1] int32[] CS$6$0000,
               [2] int32 CS$7$0001)
      IL_0000:  ldc.i4.5
      IL_0001:  newarr     [mscorlib]System.Int32
      IL_0006:  dup
      IL_0007:  ldtoken    field valuetype '<PrivateImplementationDetails>{90173B59-8615-4757-89A1-22E0F30C78E3}'/'__StaticArrayInitTypeSize=20' '<PrivateImplementationDetails>{90173B59-8615-4757-89A1-22E0F30C78E3}'::'$$method0x6000001-1'
      IL_000c:  call       void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array,
                                                                                                          valuetype [mscorlib]System.RuntimeFieldHandle)
      IL_0011:  stloc.0
      IL_0012:  ldloc.0    // 将数组对象压入栈
      IL_0013:  stloc.1    // 将数组指针赋值给位置1的变量
      IL_0014:  ldc.i4.0   // 常数0压入栈
      IL_0015:  stloc.2    // 赋值给位置2变量
      IL_0016:  br.s       IL_0020
      IL_0018:  ldloc.1
      IL_0019:  ldloc.2
      IL_001a:  ldelem.i4
      IL_001b:  pop
      IL_001c:  ldloc.2
      IL_001d:  ldc.i4.1
      IL_001e:  add
      IL_001f:  stloc.2
      IL_0020:  ldloc.2
      IL_0021:  ldloc.1
      IL_0022:  ldlen
      IL_0023:  conv.i4
      IL_0024:  blt.s      IL_0018
      IL_0026:  ret
    } // end of method Program::ILDiscoveryForeach
    
    阅读上面的代码,发现对于整数数组对象的foreach,编译器会编译成for(int i=0; i<length;i++)的方式编译。
     
      
  • 相关阅读:
    Asp.Net服务器Cassini
    Vista系统Administrator帐户的激活与禁用
    SqlServer中的Top * With Ties
    SqlServer建远程服务连接
    SqlServer2005安装成功后补加Sa用户
    列出某个表中所有的列名
    水晶报表周期性打开报表失败
    Asp.net中文cookie的乱码问题
    HTML数据库编程、JavaScript数据库编程
    试图索引的限制
  • 原文地址:https://www.cnblogs.com/quark/p/2093227.html
Copyright © 2020-2023  润新知