首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

Emit 進階教程:在動態方法中實現 Try-Catch 異常處理

     編輯:關於.NET

Reflection.Emit 生成動態類型時,異常處理塊 (try/catch/finally) 的構建方式與普通 IL 指令截然不同,需使用 ILGenerator 的三個專用方法:BeginExceptionBlock、BeginCatchBlock、EndExceptionBlock。本文以 Convert.ToInt32 安全轉換為例,完整演示 IL 級異常處理流程。

目標 C# 代碼

public static int ConvertToInt32(string str)
{
    int num = 0;
    try
    {
        num = Convert.ToInt32(str);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
    return num;
}

關鍵 API 說明

方法對應 C# 關鍵字說明
BeginExceptionBlock()try標記受保護區塊開始,返回用於跳轉的 Label
BeginCatchBlock(Type)catch (T)開始捕獲塊,參數為異常類型;此時評估棧頂為異常實例
EndExceptionBlock()} (整個 try-catch 結束)結束異常處理區塊,含所有 catch/finally

完整 Emit 實現

var typeBuilder = moduleBuilder.DefineType("ExceptionHandler", TypeAttributes.Public);
var methodBuilder = typeBuilder.DefineMethod(
    "ConvertToInt32",
    MethodAttributes.Public | MethodAttributes.Static,
    typeof(int),
    new[] { typeof(string) });

var il = methodBuilder.GetILGenerator();
var numLocal = il.DeclareLocal(typeof(int));

// int num = 0
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Stloc, numLocal);

// ---- try 區塊 ----
var tryLabel = il.BeginExceptionBlock(); // 等同 try {

// num = Convert.ToInt32(str)
il.Emit(OpCodes.Ldarg_0); // 參數 str
il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToInt32", new[] { typeof(string) }));
il.Emit(OpCodes.Stloc, numLocal);

// ---- catch (Exception ex) ----
il.BeginCatchBlock(typeof(Exception)); // 棧頂現為 ex

// Console.WriteLine(ex.Message)
il.Emit(OpCodes.Callvirt, typeof(Exception).GetProperty("Message").GetGetMethod());
il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new[] { typeof(string) }));

// ---- 結束 try-catch ----
il.EndExceptionBlock(); // 等同 }

// return num
il.Emit(OpCodes.Ldloc, numLocal);
il.Emit(OpCodes.Ret);

var type = typeBuilder.CreateType();
assemblyBuilder.Save("DynamicAssembly.dll");

執行流程圖解

Try Block:
  Ldarg_0
  Call Convert.ToInt32
  Stloc num
  Leave -> EndTryLabel

Catch Block (Exception on stack):
  Callvirt Exception.get_Message
  Call Console.WriteLine
  Leave -> EndTryLabel

EndTryLabel:
  Ldloc num
  Ret

進階:多 Catch 塊與 Finally

// 多 catch
il.BeginCatchBlock(typeof(FormatException));
// 處理格式錯誤
il.BeginCatchBlock(typeof(OverflowException));
// 處理溢出
il.BeginCatchBlock(typeof(Exception));
// 兜底

// Finally (需在所有 catch 之後,EndExceptionBlock 之前)
il.BeginFinallyBlock();
// 清理代碼
il.EndExceptionBlock();

常見坑點

  1. 棧平衡:進入 catch 時棧頂自動放入異常對象,離開前必須消費掉 (調用屬性/方法或 pop),否則驗證失敗。
  2. Leave vs Br:在 try/catch 內部跳轉到區塊外必須用 OpCodes.Leave (或 Leave_S),它會自動執行 finally 邏輯;Br 會導致驗證錯誤。
  3. PEVerify:生成組件後務必運行 peverify.exe DynamicAssembly.dll 檢查 IL 合法性。

掌握異常塊發射,才能構建健壯的動態代理、攔截器與編譯器後端。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved