quark

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  49 随笔 :: 10 文章 :: 40 评论 :: 19万 阅读
< 2025年4月 >
30 31 1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 1 2 3
4 5 6 7 8 9 10

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

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

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

于是自己做了如下实验:

1
2
3
4
5
6
7
8
static void ILDiscoveryForeach()
{
    var someList = Enumerable.Range(0, 10);
    foreach (var item in someList)
    {
 
    }
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
.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
  {
1
2
3
4
5
6
7
8
9
10
11
    // 如果枚举器不为空,则调用枚举器的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对象!!
1
  
1
这样一切都很合理了。
1
  
1
但是对于一个数组对象的foreach遍历又是怎样的呢?
1
代码:
1
2
3
4
5
6
7
8
static void ILDiscoveryForeach()
{
    int[] someList = { 1, 2, 3, 4, 5 };
    foreach (var item in someList)
    {
 
    }
}

IL:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
.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++)的方式编译。
1
  
1
   
posted on   QuarkZ  阅读(401)  评论(2编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示