为什么不看看你实际得到了什么?
这是一段简单的C#代码:
static void Main(string[] args)
{
int i = 0;
try
{
i = 1;
Console.WriteLine(i);
return;
}
finally
{
Console.WriteLine("finally.");
}
}
下面是调试构建中生成的 IL 代码:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 1
.locals init ([0] int32 i)
L_0000: nop
L_0001: ldc.i4.0
L_0002: stloc.0
L_0003: nop
L_0004: ldc.i4.1
L_0005: stloc.0
L_0006: ldloc.0 // here's the WriteLine of i
L_0007: call void [mscorlib]System.Console::WriteLine(int32)
L_000c: nop
L_000d: leave.s L_001d // this is the flavor of branch that triggers finally
L_000f: nop
L_0010: ldstr "finally."
L_0015: call void [mscorlib]System.Console::WriteLine(string)
L_001a: nop
L_001b: nop
L_001c: endfinally
L_001d: nop
L_001e: ret
.try L_0003 to L_000f finally handler L_000f to L_001d
}
这是在调试模式下运行时JIT生成的汇编代码:
00000000 push ebp
00000001 mov ebp,esp
00000003 push edi
00000004 push esi
00000005 push ebx
00000006 sub esp,34h
00000009 mov esi,ecx
0000000b lea edi,[ebp-38h]
0000000e mov ecx,0Bh
00000013 xor eax,eax
00000015 rep stos dword ptr es:[edi]
00000017 mov ecx,esi
00000019 xor eax,eax
0000001b mov dword ptr [ebp-1Ch],eax
0000001e mov dword ptr [ebp-3Ch],ecx
00000021 cmp dword ptr ds:[00288D34h],0
00000028 je 0000002F
0000002a call 59439E21
0000002f xor edx,edx
00000031 mov dword ptr [ebp-40h],edx
00000034 nop
int i = 0
00000035 xor edx,edx
00000037 mov dword ptr [ebp-40h],edx
try
{
0000003a nop
i = 1
0000003b mov dword ptr [ebp-40h],1
Console.WriteLine(i)
00000042 mov ecx,dword ptr [ebp-40h]
00000045 call 58DB2EA0
0000004a nop
return
0000004b nop
0000004c mov dword ptr [ebp-20h],0
00000053 mov dword ptr [ebp-1Ch],0FCh
0000005a push 4E1584h
0000005f jmp 00000061
}
finally
{
00000061 nop
Console.WriteLine("finally.")
00000062 mov ecx,dword ptr ds:[036E2088h]
00000068 call 58DB2DB4
0000006d nop
}
0000006e nop
0000006f pop eax
00000070 jmp eax
00000072 nop
}
00000073 nop
00000074 lea esp,[ebp-0Ch]
00000077 pop ebx
00000078 pop esi
00000079 pop edi
0000007a pop ebp
0000007b ret
0000007c mov dword ptr [ebp-1Ch],0
00000083 jmp 00000072
现在,如果我注释掉try、finally和return,那么通过JIT编译得到的汇编代码将几乎相同。你会看到的区别是跳转到finally块并执行一些代码来确定在finally执行后要去哪里。所以你说的只是微小的差异。在发布版本中,跳转到finally的指令将被优化掉 - 大括号是nop指令,因此这将变成跳转到下一条指令,它也是一个nop - 这是一个简单的peephole优化。pop eax然后jmp eax也是廉价的。
{
00000000 push ebp
00000001 mov ebp,esp
00000003 push edi
00000004 push esi
00000005 push ebx
00000006 sub esp,34h
00000009 mov esi,ecx
0000000b lea edi,[ebp-38h]
0000000e mov ecx,0Bh
00000013 xor eax,eax
00000015 rep stos dword ptr es:[edi]
00000017 mov ecx,esi
00000019 xor eax,eax
0000001b mov dword ptr [ebp-1Ch],eax
0000001e mov dword ptr [ebp-3Ch],ecx
00000021 cmp dword ptr ds:[00198D34h],0
00000028 je 0000002F
0000002a call 59549E21
0000002f xor edx,edx
00000031 mov dword ptr [ebp-40h],edx
00000034 nop
int i = 0
00000035 xor edx,edx
00000037 mov dword ptr [ebp-40h],edx
//try
//{
i = 1
0000003a mov dword ptr [ebp-40h],1
Console.WriteLine(i)
00000041 mov ecx,dword ptr [ebp-40h]
00000044 call 58EC2EA0
00000049 nop
// return
//}
//finally
//{
Console.WriteLine("finally.")
0000004a mov ecx,dword ptr ds:[034C2088h]
00000050 call 58EC2DB4
00000055 nop
//}
}
00000056 nop
00000057 lea esp,[ebp-0Ch]
0000005a pop ebx
0000005b pop esi
0000005c pop edi
0000005d pop ebp
0000005e ret
所以你说的是try/finally的代价非常小。在很少的问题领域中,这点会有影响。如果你在做类似于memcpy的操作,并且在每个要复制的字节周围放置一个try/finally,然后继续复制数百MB的数据,我可以看出那可能是一个问题,但在大多数情况下,代价可以忽略不计。