zoukankan      html  css  js  c++  java
  • 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++)的方式编译。
     
      
  • 相关阅读:
    国庆后的星期一
    如何让百度快速收录文章
    牛大发了~美国12岁女孩自制"火箭"将Hello Kitty送上近太空
    免费CDN /初体验 访问量激升19%
    国外免费CDN CloudFlare申请教程
    Windows Azure Application申请方法
    坚持转自网易轻博客LOFTER
    玩转你的Gravatar全球通用头像
    IIS下配置WordPress永久链接支持中文完美方法
    常用的SqlHelper类
  • 原文地址:https://www.cnblogs.com/quark/p/2093227.html
Copyright © 2011-2022 走看看