今天在阅读《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++)的方式编译。