.NET Emit 入門教程:第五部分:動態生成方法(MethodBuilder 與 DynamicMethod)

前言:

當我們涉及到在運行時生成和定義方法時,便需要使用到C#中的兩個關鍵類之一:MethodBuilder 或 DynamicMethod。

這兩者都屬於反射(Reflection.Emit)的一部分,允許我們以動態的方式創建方法。

兩者各有側重,使用方式大體相同,本篇文章我們先介紹 MethodBuilder,再介紹 DynamicMethod,最後再總結兩者的區別。

1、MethodBuilder 介紹:

MethodBuilder 是一個強大的工具,用於在動態程序集中創建方法。

如果你需要構建整個類型(包括字段、屬性、方法等),那麼按流程:

首先你需要創建一個動態程序集(AssemblyBuilder),

然後在其中創建一個模塊(ModuleBuilder),

最後再創建一個或多個類型(TypeBuilder)。

而要在這些類型中創建方法,就可以使用 MethodBuilder

其關鍵特點:

  • 綁定到類型MethodBuilder 創建的方法是屬於某個類型的一部分,因此只能通過該類型的實例或靜態引用來調用。
  • 方法簽名:需要指定方法名稱、參數類型和返回類型。
  • IL代碼生成:需要手動編寫IL代碼。

2、MethodBuilder 代碼:定義方法

正如上文所說,MethodBuilder 的使用,需要定義整個程序集上下文。

因此我們先編寫一下共用部分代碼,同樣用於( .NET 版本生成程序集,以便對照生成代碼):

AssemblyName assName = new AssemblyName("myAssembly") { Version = new Version("1.1.1.2") };
AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(assName, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder mb = ab.DefineDynamicModule("myModule", "b.dll");
TypeBuilder tb = mb.DefineType("MyNameSpace.MyClass", TypeAttributes.Public | TypeAttributes.Class);


//定義方法......


tb.CreateType();
ab.Save("b.dll");

方法定義的過程:

1、通過 TypeBuilder 的 DefineMethod 來定義方法:MethodBuilder methodBuilder = tb.DefineMethod("方法名",......);
2、通過構造函數,可以設定方法定義的參數;
3、也可以後面再通過 MethodBuilder 實例 的 SetXXX 及  系列來定義參數。

下面示例展示方法的定義:

A、定義實例方法:使用簡單參數

 //定義實例方法:通過構造函數指定方法修飾符:Public、返回值:typeof(string)、參數類型:typeof(int),typeof(string)
 MethodBuilder methodBuilder = tb.DefineMethod("MyMethod", MethodAttributes.Public, typeof(string), new Type[] { typeof(int), typeof(string) });
//定義參數的名稱:指定參數名稱:id,name methodBuilder.DefineParameter(
1, ParameterAttributes.None, "id"); methodBuilder.DefineParameter(2, ParameterAttributes.None, "name"); //用IL編寫方法實現 var il = methodBuilder.GetILGenerator(); il.EmitWriteLine("hello world!"); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ret);
對照生成的代碼:

注意事項:

1:DefineParameter 對參數的索引從1開始(對實例方法或靜態方法都一樣)。
2:IL 構建代碼時:實例方法下 Ldarg_0 是 this 自身,靜態方法下 Ldarg_0 是id參數。

B、定義靜態方法:參數進階【包括ref、out、指針】大雜繪

//定義靜態方法
MethodBuilder methodBuilder = tb.DefineMethod("MyMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(object), new Type[] { typeof(int).MakeByRefType(), typeof(string).MakePointerType() });
methodBuilder.DefineParameter(1, ParameterAttributes.None, "id");
methodBuilder.DefineParameter(2, ParameterAttributes.Out | ParameterAttributes.Optional, "name");

var il = methodBuilder.GetILGenerator();
il.EmitWriteLine("hello world!");
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ret);

對照生成的代碼:

簡要說明:

1、ref 參數定義:在構造函數類型用:typeof(int).MakeByRefType()
2、指針 參數定義:在構造函數類型用:typeof(int).MakePointerType()
3、out 參數定義:在 DefineParameter 方法的 ParameterAttributes.Out 參數指定。
注意事項:在靜態方法中,IL 代碼 Ldarg_0 指向第一個參數 id。

C、定義方法:添加特性

 // 添加自定義特性
 ConstructorInfo attributeConstructor = typeof(AssemblyTitleAttribute).GetConstructor(new Type[] { typeof(string) });
 CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(attributeConstructor, new object[] { "ExampleAttribute" });
 methodBuilder.SetCustomAttribute(attributeBuilder);

對照生成的代碼:

3、MethodBuilder 代碼:方法的動態調用

對於運行時生成的動態方法實現動態調用,可以通過反射調用。

下面演示(將方法變更爲實例方法,使用反射調用):

通過獲取類的定義Type classType,然後Type.GetMethod獲取方法進行調用,並傳進創建的實例作爲參數之一。

當然,如果方法調用頻率很高,要更進一步,也可以爲 MethodInfo 創建委託,來實現更高效率的調用。 

然而:

Delegate.CreateDelegate(......) 只支持從靜態方法轉委託方法,不支持實例方法。 

所以:

默認是不能實現對實例 MethodInfo 轉委託的高效調用的。

但,那是默認,經過本人的百日創新,結合 DynamicMethod,還是有辦法實現的。

實現方式可以參考本人的開源框架 Taurus.MVC 中關於 Deletgate 的相關實現。

既然提到了 DynamicMethod,那下面就開始介紹它了。

4、DynamicMethod 介紹:

DynamicMethod 則更加靈活。它允許在運行時生成和執行方法,而無需創建動態程序集或動態類型來容納該方法。

這意味着你可以直接生成和執行少量代碼,而不必擔心整個類型的構建。

關鍵特點:

  • 不綁定到類型DynamicMethod 不屬於特定的類型,因此可以在任何上下文中調用。
  • 性能:通常比 MethodBuilder 更高效。

5、DynamicMethod 代碼:

 //創建動態方法
 DynamicMethod dynamicMethod = new DynamicMethod("MyMethod", typeof(void), null);
 var il = dynamicMethod.GetILGenerator();
 il.EmitWriteLine("hello world!");
 il.Emit(OpCodes.Ret);

 //創建調用委託
 var deletegateMethod = dynamicMethod.CreateDelegate(typeof(Action)) as Action;
 //執行委託
 deletegateMethod();

執行結果:

簡要說明:

1、DynamicMethod 擁有的基礎定義方法和 MethodBuilder 基本一致。
2、DynamicMethod 動態方法關注點是運行時執行,因此,可以省略很多參數的定義,只需要定義最簡單的方法名、返回值、輸入參數。
3、DynamicMethod 默認創建的靜態方法,因此,它可以默認擁有 CreateDelegate 來實現高效調用。

6、DynamicMethod 與 MethodBuilder 兩者的區別:

在C#中,MethodBuilderDynamicMethod都屬於反射發射(Reflection.Emit)的一部分,用於在運行時生成和定義方法。

讓我們來簡要介紹一下它們之間的區別:

  1. DynamicMethod:

    • DynamicMethod類允許在運行時生成和執行方法,而無需創建動態程序集或動態類型來容納該方法。
    • 由即時(JIT)編譯器生成的可執行代碼在DynamicMethod對象被回收時也會被回收。
    • 動態方法是生成和執行少量代碼的最有效方式。
    • 如果需要動態創建一個或多個方法,應使用 DynamicMethod
  2. MethodBuilder:

    • MethodBuilder用於在動態程序集中創建方法。
    • 如果要創建整個類型(包括字段、屬性、方法等),則需要先創建一個動態程序集(AssemblyBuilder),然後在其中創建一個模塊(ModuleBuilder),最後再創建一個或多個類型(TypeBuilder)。
    • 若要在這些類型中創建方法,可以使用MethodBuilder

通俗的講人話即是:

1、如果是生成動態程序集,包括創建動態類,那麼使用 MethodBuilder。

2、如果只是定義動態方法供調用,使用 DynamicMethod,因爲它不用定義整個程序集,直接起手就是方法。

3、使用委託調用方法:MethodBuilder 和 DynamicMethod 都支持,但 DynamicMethod 直接提供 CreateDelegate 方法,方便起手調用。

總結:

在本文的第五部分中,我們深入探討了 .NET Emit 中動態生成方法的兩種方式:MethodBuilder 和 DynamicMethod。

通過 MethodBuilder,我們可以在運行時動態創建和定義方法,爲其添加參數、自定義屬性等元數據信息。

而 DynamicMethod 則提供了一種更靈活的動態方法生成方式,特別適合於需要高性能的動態代碼生成場景。

在本篇教程中,我們學習瞭如何使用 MethodBuilder 來創建動態方法,並且演示瞭如何定義帶有引用參數的動態方法以及如何向動態方法添加自定義屬性。

這些內容對於理解動態方法的創建和擴展具有重要意義。

在下一篇中,我們將重點講述IL語言,IL(Intermediate Language)是.NET平臺上的一種中間語言,它是由C#、VB.NET等高級語言編譯成的一種低級語言表示形式。

我們將會詳細介紹IL語言的基本結構、指令集、堆棧操作等內容,幫助讀者更深入地理解.NET動態方法的內部實現和執行過程。

通過深入瞭解IL語言,讀者將能夠更好地掌握.NET平臺上動態代碼生成的技術,並且能夠對IL代碼進行優化和調試,從而更好地應用於實際的軟件開發項目中。

敬請期待下一篇教程的發佈,讓我們一起探究IL語言的奧祕吧!

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