前言:
經過前面幾大部分的學習,已經掌握了 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):
條件跳轉指令:
beq
:如果兩個值相等,則跳轉到指定的標籤。bge
:如果第一個值大於或等於第二個值,則跳轉到指定的標籤。bgt
:如果第一個值大於第二個值,則跳轉到指定的標籤。ble
:如果第一個值小於或等於第二個值,則跳轉到指定的標籤。blt
:如果第一個值小於第二個值,則跳轉到指定的標籤.bne.un
:如果兩個無符號整數值不相等,則跳轉到指定的標籤。brtrue
:如果值為 true,則跳轉到指定的標籤。brfalse
:如果值為 false,則跳轉到指定的標籤。brtrue.s
:如果值為 true,則跳轉到指定的標籤(短格式)。brfalse.s
:如果值為 false,則跳轉到指定的標籤(短格式).
無條件跳轉指令:
br
:無條件跳轉到指定的標籤。br.s
:短格式的無條件跳轉到指定的標籤。leave
:無條件跳轉到 try、filter 或 finally 塊的末尾。leave.s
:短格式的無條件跳轉到 try、filter 或 finally 塊的末尾.
比較跳轉指令:
bgt.un
:如果第一個無符號整數值大於第二個值,則跳轉到指定的標籤。bge.un
:如果第一個無符號整數值大於或等於第二個值,則跳轉到指定的標籤。blt.un
:如果第一個無符號整數值小於第二個值,則跳轉到指定的標籤。ble.un
:如果第一個無符號整數值小於或等於第二個值,則跳轉到指定的標籤.
其他跳轉指令:
switch
:根據給定的索引值跳轉到不同的標籤。brnull
:如果值為 null,則跳轉到指定的標籤。brinst
:如果物件是類的例項,則跳轉到指定的標籤。
這些指令可以幫助控制流程,在特定條件下跳轉到指定的標籤位置執行相應的程式碼。
透過合理使用這些跳轉指令,可以實現複雜的邏輯控制和條件判斷。
總結:
這篇教程總結了.NET Emit 中關於 IL 指令的第六部分,著重介紹了 ILGenerator 輔助方法的詳細內容。
ILGenerator 是在動態生成程式集時用來生成 Intermediate Language(IL)指令的一個重要工具。
讀者透過本篇文章,可以迅速瞭解到該教程的主要內容和重點,更好地掌握 ILGenerator 輔助方法的使用及 IL 指令的生成過程。
下一篇,我們將重點講解 IL 的指令內容。