今天在阅读《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 | |
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步