本文通過一個簡單的示例來說明在IL中進行異常處理時要注意的關鍵點。
我們來看一個包含try...catch...finally的示例:
public void TestEF()
{
TransactionScopeFactory factory = new TransactionScopeFactory(null);
TransactionScope scope = factory.NewTransactionScope(false);
try
{
scope.Commit();
}
catch (Exception ee)
{
string msg = ee.Message;
finally
scope.Dispose();
}
這段代碼實際上與使用using是等價的:
public void TestEF()
{
TransactionScopeFactory factory = new TransactionScopeFactory(null);
using (TransactionScope scope = factory.NewTransactionScope(false))
{
try
{
scope.Commit();
catch (Exception ee)
string msg = ee.Message;
}
}
它們對應的IL代碼如下所示:
.method public hidebysig instance void TestEF() cil managed
.maxstack 2
.locals init (
[0] class [DataRabbit.Application]DataRabbit.Application.TransactionScopeFactory factory,
[1] class [DataRabbit.Application]DataRabbit.Application.TransactionScope scope,
[2] class [mscorlib]System.Exception ee,
[3] string msg)
L_0000: nop
L_0001: ldnull
L_0002: newobj instance void [DataRabbit.Application]DataRabbit.Application.TransactionScopeFactory::.ctor(class [DataRabbit]DataRabbit.DataConfiguration)
L_0007: stloc.0
L_0008: ldloc.0
L_0009: ldc.i4.0
L_000a: callvirt instance class [DataRabbit.Application]DataRabbit.Application.TransactionScope [DataRabbit.Application]DataRabbit.Application.TransactionScopeFactory::NewTransactionScope(bool)
L_000f: stloc.1
L_0010: nop
L_0011: ldloc.1
L_0012: callvirt instance void [DataRabbit.Application]DataRabbit.Application.TransactionScope::Commit()
L_0017: nop
L_0018: nop
L_0019: leave.s L_0027
L_001b: stloc.2
L_001c: nop
L_001d: ldloc.2
L_001e: callvirt instance string [mscorlib]System.Exception::get_Message()
L_0023: stloc.3
L_0024: nop
L_0025: leave.s L_0027
L_0027: nop
L_0028: leave.s L_0034
L_002a: nop
L_002b: ldloc.1
L_002c: callvirt instance void [DataRabbit.Application]DataRabbit.Application.TransactionScope::Dispose()
L_0031: nop
L_0032: nop
L_0033: endfinally
L_0034: nop
L_0035: ret
.try L_0010 to L_001b catch [mscorlib]System.Exception handler L_001b to L_0027
.try L_0010 to L_002a finally handler L_002a to L_0034
我們來剖析這段IL中的異常處理流程:
1.有最後的兩句代碼,我們看到:
(1)try...catch...finally 是由try...catch 和 try...finally兩部分構成。
(2)try...finally 中的try塊内含了catch塊。
即類似這樣:
.try
{
.try
{
}
catch
}
finally
2.try塊、catch塊(catch handler)隻能通過leave(或leave.s)退出。
3.finally塊(finally handler)必須通過endfinally退出。
4.由于try...catch 和 try...finally兩部分都需要退出try塊,是以我們看到在L_0019 和 L_0028 處都有對應的leave.s指令。
5.程式中如果沒有finally塊,則IL中隻需要處理try...catch 部分;同理,如果程式中沒有catch塊,則IL隻需要處理try...finally部分。
6.總結起來,IL的異常處理類似這個樣子:
leave L1
leave L2
endfinally