天天看點

【Emit基礎】在IL中進行異常處理

     本文通過一個簡單的示例來說明在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