.NET Emit 入門教程:第六部分:IL 指令:2:詳解 ILGenerator 輔助方法

前言:

經過前面幾大部分的學習,已經掌握了 Emit 的前因後果,今天來詳細講解 Emit 中 IL 的部分內容。

如前文所講,通過 DynamicMethod(或 MethodBuilder)可獲得 ILGenerator 這個用於編寫 IL 指令的類,並用它來編寫 IL 指令。

本篇主要講解 ILGenerator 的介紹,以及主要的輔助方法,詳細的指令方法,則拆分到下一篇介紹。

下面就開邕它的介紹吧:

1、ILGenerator 介紹

ILGenerator 是.NET 中的一個重要組件,用於動態生成 Intermediate Language(IL)代碼。

通過ILGenerator,開發人員可以在運行時創建和修改方法體內的IL指令,實現動態方法的生成和優化。

ILGenerator 提供了一組方法,允許程序員發出各種IL指令,包括加載、存儲、運算、流程控制等操作,從而實現對方法體邏輯的靈活控制。

在 .NET 開發中,ILGenerator 通常與 DynamicMethod 類結合使用。

通過 DynamicMethod 創建動態方法對象,然後使用 ILGenerator 在其中生成IL代碼。

這種結合使開發人員能夠在運行時動態生成高效的代碼,應用於一些需要動態生成代碼的場景,如動態代理、AOP等。

ILGenerator 的靈活性和強大功能爲.NET開發提供了更多可能性和自定義性。

2、ILGenerator 簡單示例:

先看一下之前文章提到的代碼:

 DynamicMethod dynamicMethod = new DynamicMethod("MyMethod", typeof(void), null);
 ILGenerator il = dynamicMethod.GetILGenerator();
 il.EmitWriteLine("hello world!");
 il.Emit(OpCodes.Ret);

從示例代碼使用了兩類方法:

指令方法:il.Emit(OpCodes.Ret)
輔助方法:il.EmitWriteLine("hello world!")

所有的輔助方法,都是基於指令方法的封裝,即用指令也可以實現該方法功能,

但用輔助方法,可以更簡單的調用,下面開始介紹輔助方法。

3、ILGenerator 輔助方法:EmitWriteLine

該方法封裝好的調用 WriteLine 輸出控制檯消息,使用它可以簡單輸出控制檯方法,而不用編寫 Emit 指令方法。

如果用 Emit 指令,編寫是這樣的:

            var il = methodBuilder.GetILGenerator();

            il.Emit(OpCodes.Ldstr, "這是一個示例消息");
            il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));

            il.Emit(OpCodes.Ret);

實現效果對應代碼:

4、ILGenerator 輔助方法:異常處理 try catch finally

輔助方法中,提供了關於常用的 try catch finally 的封裝方法,可以幫助我們更簡單的編寫IL方法:

看示例代碼:

 var il = methodBuilder.GetILGenerator();


 il.BeginExceptionBlock();// 開始 try
 il.EmitWriteLine("hello world!");

 il.BeginCatchBlock(typeof(Exception));// 開始 catch
 il.EmitWriteLine("hello world on error!");

 il.BeginFinallyBlock();// 開始 finally
 il.EmitWriteLine("hello world on finally!");

 il.EndExceptionBlock();// 結束


 il.Emit(OpCodes.Ldarg_0);
 il.Emit(OpCodes.Ret);

參照對應生成的代碼:

5、ILGenerator 輔助方法:異常拋出

輔助方法中,也提供了一個拋出異常的方法,示例代碼:

var il = methodBuilder.GetILGenerator();

il.ThrowException(typeof(Exception));

il.Emit(OpCodes.Ret);

查看對應生成:

但是該輔助方法只能生成拋出異常,沒有提供異常的參數。

如果需要更詳細的異常拋出,則需要使用指令的方法:

指令方法如:

var il = methodBuilder.GetILGenerator();

il.Emit(OpCodes.Ldstr, "這是一個示例異常消息");

// 創建一個新的 Exception 實例
ConstructorInfo ctor = typeof(Exception).GetConstructor(new Type[] { typeof(string) });
il.Emit(OpCodes.Newobj, ctor);

// 使用 ThrowException 方法引發異常
il.Emit(OpCodes.Throw);


il.Emit(OpCodes.Ret);

生成的對應代碼:

6、ILGenerator 輔助方法:定義變量

輔助方法中,有一個是用來輔助定義變量的。

但是它需要配置 Emit 指令使用,示例代碼:

ILGenerator il = methodBuilder.GetILGenerator();

LocalBuilder local = il.DeclareLocal(typeof(string)); // 定義變量

il.Emit(OpCodes.Ldstr,"hello world"); // 加載字符串
il.Emit(OpCodes.Stloc, local); // 將字符串賦值給變量

il.Emit(OpCodes.Ldloc, local); //從變量中 加載值進棧
il.Emit(OpCodes.Ret);//返回(帶值)

對應生成的代碼:

在這個示例中(爲了舉例,多了中間的賦值取值的過程):

可以看出,定義的臨時變量,都是沒有名稱的,只有類型。

它可以用來臨時存值,需要用到的時候,再將值取出,對應兩個 Emit 指令:

賦值:il.Emit(OpCodes.Stloc, local); // 將字符串賦值給變量
取值:il.Emit(OpCodes.Ldloc, local); //從變量中 加載值進棧

7、ILGenerator 輔助方法:定義標籤

標籤的定義,可以理解爲跳轉,即現在不常用的 goto 語句所需的的標籤。

標籤的定義,在 if else 中, switch 中,for 循環中,都會常常使用到標籤。

標籤的使用分爲3步,定義標籤、設定標籤、跳轉到標籤。

標籤定義:

Label label = il.DefineLabel();

設定標籤:

il.MarkLabel(label);

跳轉標籤:

在 IL(Intermediate Language)中,可以使用以下指令來跳轉到標籤(Label):

條件跳轉指令:

  1. beq:如果兩個值相等,則跳轉到指定的標籤。
  2. bge:如果第一個值大於或等於第二個值,則跳轉到指定的標籤。
  3. bgt:如果第一個值大於第二個值,則跳轉到指定的標籤。
  4. ble:如果第一個值小於或等於第二個值,則跳轉到指定的標籤。
  5. blt:如果第一個值小於第二個值,則跳轉到指定的標籤.
  6. bne.un:如果兩個無符號整數值不相等,則跳轉到指定的標籤。
  7. brtrue:如果值爲 true,則跳轉到指定的標籤。
  8. brfalse:如果值爲 false,則跳轉到指定的標籤。
  9. brtrue.s:如果值爲 true,則跳轉到指定的標籤(短格式)。
  10. brfalse.s:如果值爲 false,則跳轉到指定的標籤(短格式).

無條件跳轉指令:

  1. br:無條件跳轉到指定的標籤。
  2. br.s:短格式的無條件跳轉到指定的標籤。
  3. leave:無條件跳轉到 try、filter 或 finally 塊的末尾。
  4. leave.s:短格式的無條件跳轉到 try、filter 或 finally 塊的末尾.

比較跳轉指令:

  1. bgt.un:如果第一個無符號整數值大於第二個值,則跳轉到指定的標籤。
  2. bge.un:如果第一個無符號整數值大於或等於第二個值,則跳轉到指定的標籤。
  3. blt.un:如果第一個無符號整數值小於第二個值,則跳轉到指定的標籤。
  4. ble.un:如果第一個無符號整數值小於或等於第二個值,則跳轉到指定的標籤.

其他跳轉指令:

  1. switch:根據給定的索引值跳轉到不同的標籤。
  2. brnull:如果值爲 null,則跳轉到指定的標籤。
  3. brinst:如果對象是類的實例,則跳轉到指定的標籤。

這些指令可以幫助控制流程,在特定條件下跳轉到指定的標籤位置執行相應的代碼。

通過合理使用這些跳轉指令,可以實現複雜的邏輯控制和條件判斷。

總結:

這篇教程總結了.NET Emit 中關於 IL 指令的第六部分,着重介紹了 ILGenerator 輔助方法的詳細內容。

ILGenerator 是在動態生成程序集時用來生成 Intermediate Language(IL)指令的一個重要工具。

讀者通過本篇文章,可以迅速瞭解到該教程的主要內容和重點,更好地掌握 ILGenerator 輔助方法的使用及 IL 指令的生成過程。

下一篇,我們將重點講解 IL 的指令內容。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章