.NET Emit 入門教程:第四部分:構建類型(Type)

前言:

在動態生成代碼的過程中,構建類型(Type)是至關重要的一步。

通過使用 Emit 中的 TypeBuilder,我們可以定義和創建各種類型,包括類、結構體和接口。

本節將深入探討如何使用 TypeBuilder 動態構建類型,並介紹其在實際應用中的重要性。

定義公用代碼,生成程序集以供對照:

通過學習本系列之前的文章,我們可以輕鬆定義 AssemblyBuilder 程序集構建器,再通過程序集構建器,定義 ModuleBuilder 模塊構建器。

下面我們先通過定義公用代碼來生成程序集,以便更好的通過反編繹,來觀察對照我們生成的代碼。

AssemblyName assName = new AssemblyName("myAssembly");
AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(assName, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder mb = ab.DefineDynamicModule("myModule","a.dll");

//...今天的示例代碼存放地


ab.Save("a.dll");

注意標紅的部分爲 .NET 版本代碼,正如本系列之前文件所說,只有 .NET 版本支持程序集持久化,.NET Core 需要到9版本才支持。

.NET Core 用 AssemblyBuilder.DefineDynamicAssembly來構建。

ModuleBuilder 的幾個定義方法:

1、定義枚舉:

EnumBuilder eb=mb.DefineEnum("bbb", ...);

2、定義類(包括類、接口、結構體):

TypeBuilder tb=mbDefineType("aaa", ...);

3、定義內部類:

TypeBuilder tb=mbDefineType("aaa", ...);
TypeBuilder innerClassBuilder = tb.DefineNestedType("innerClass",...);

下面我們使用代碼對照,來學習本節內容:

1、定義枚舉:

EnumBuilder eb = mb.DefineEnum("MyNameSpace.MyEnum", TypeAttributes.Public, typeof(int));

eb.DefineLiteral("Spring", 0);
eb.DefineLiteral("Summer", 1);
eb.DefineLiteral("Autumn", 2);
eb.DefineLiteral("Winter", 3);

Type enumType = eb.CreateType();

對應生成的代碼:

2、定義接口:

 TypeBuilder tb = mb.DefineType("MyNameSpace.MyInterface", TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.Interface);

 //tb.DefineField("ID", typeof(int), FieldAttributes.Public| FieldAttributes.Static| FieldAttributes.InitOnly);

 // 定義屬性 "Name",類型爲 int
 PropertyBuilder propertyBuilder = tb.DefineProperty("Name", PropertyAttributes.None, typeof(int), null);

 // 定義屬性的 getter 方法
 MethodBuilder getterMethodBuilder = tb.DefineMethod("get_Name", MethodAttributes.Public | MethodAttributes.Abstract | MethodAttributes.Virtual, typeof(int), Type.EmptyTypes);
 propertyBuilder.SetGetMethod(getterMethodBuilder);

 // 定義屬性的 setter 方法
 MethodBuilder setterMethodBuilder = tb.DefineMethod("set_Name", MethodAttributes.Public | MethodAttributes.Abstract | MethodAttributes.Virtual, null, new Type[] { typeof(int) });
 propertyBuilder.SetSetMethod(setterMethodBuilder);
 
//定義方法 GetMyName MethodBuilder getMyName
= tb.DefineMethod("GetMyName", MethodAttributes.Public | MethodAttributes.Abstract | MethodAttributes.Virtual, typeof(string), new Type[] { typeof(int) }); tb.CreateType();

屬性的定義,需要掛接 get_XXX 和 set_XXX 兩個方法,會相對顯的定義麻煩了點。

對應生成的代碼:

3、定義結構體

// 定義結構體
TypeBuilder tb = mb.DefineType("MyNameSpace.MyStruct", TypeAttributes.SequentialLayout | TypeAttributes.Public | TypeAttributes.Sealed, typeof(ValueType));

// 定義字段
tb.DefineField("ID", typeof(int), FieldAttributes.Public);
tb.DefineField("Name", typeof(string), FieldAttributes.Public);

tb.CreateType();

對應生成的代碼:

4、定義類:抽象類

 TypeBuilder tb = mb.DefineType("MyNameSpace.MyClassBase", TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.Class);
 tb.DefineField("ID", typeof(int), FieldAttributes.Public);
 tb.DefineMethod("MyProtectedMethod", MethodAttributes.Family | MethodAttributes.Abstract | MethodAttributes.Virtual, typeof(void), Type.EmptyTypes);
 tb.CreateType();


 tb.CreateType();

MethodAttributes.Family 對應的即:protected 修飾符。

對應生成的代碼:

5、定義類:並繼承自抽象類:

//定義基類
TypeBuilder tb = mb.DefineType("MyNameSpace.MyClassBase", TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.Class); tb.DefineField("ID", typeof(int), FieldAttributes.Public); tb.DefineMethod("MyProtectedMethod", MethodAttributes.Family | MethodAttributes.Abstract | MethodAttributes.Virtual, typeof(void), Type.EmptyTypes); Type typeBase = tb.CreateType();
//定義子類,繼承基類 TypeBuilder tbClass
= mb.DefineType("MyNameSpace.MyClass", TypeAttributes.Public | TypeAttributes.Class, typeBase); //實現抽象方法 MethodBuilder mbClass = tbClass.DefineMethod("MyProtectedMethod", MethodAttributes.Family | MethodAttributes.Virtual, typeof(void), Type.EmptyTypes); ILGenerator iL = mbClass.GetILGenerator(); iL.Emit(OpCodes.Ret); tbClass.CreateType();

紅色標註爲指定繼承,接口繼承一樣在該參數指定。

對應生成的代碼:

6、定義類:增加泛型參數指定

//定義基類
TypeBuilder tb = mb.DefineType("MyNameSpace.MyClassBase", TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.Class);
tb.DefineField("ID", typeof(int), FieldAttributes.Public);
tb.DefineMethod("MyProtectedMethod", MethodAttributes.Family | MethodAttributes.Abstract | MethodAttributes.Virtual, typeof(void), Type.EmptyTypes);

Type typeBase = tb.CreateType();

//定義子類繼承基類
TypeBuilder tbClass = mb.DefineType("MyNameSpace.MyClass", TypeAttributes.Public | TypeAttributes.Class, typeBase);
//實現抽象方法
MethodBuilder mbClass = tbClass.DefineMethod("MyProtectedMethod", MethodAttributes.Family | MethodAttributes.Virtual, typeof(void), Type.EmptyTypes);
ILGenerator iL = mbClass.GetILGenerator();
iL.Emit(OpCodes.Ret);


// 定義泛型參數
string[] typeParamNames = { "T" };
GenericTypeParameterBuilder[] typeParams = tbClass.DefineGenericParameters(typeParamNames);

//定義泛型方法
MethodBuilder methodBuilder = tbClass.DefineMethod("GetT", MethodAttributes.Public, typeParams[0], new Type[] { typeof(object) });
ILGenerator ilGenerator = methodBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ret);


tbClass.CreateType();

這裏通過定義泛型參數,來指定我們的泛型類。

對應生成的代碼:

7、通過內部類定義委託:

// 定義內部類,並在內部類中定義委託類型
TypeBuilder delegateBuilder = tbClass.DefineNestedType("MyNameSpace.AuthDelegate", TypeAttributes.Class | TypeAttributes.NestedPublic | TypeAttributes.Sealed, typeof(MulticastDelegate));

// 添加委託的構造函數
ConstructorBuilder constructor = delegateBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { typeof(object), typeof(IntPtr) });
constructor.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);

// 添加Invoke方法
delegateBuilder.DefineMethod("Invoke", MethodAttributes.Public, typeof(bool), new Type[] { typeof(string) }).SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);

// 創建內部類和委託類型
Type authDelegateType = delegateBuilder.CreateType();

注意,這裏是通過Type的形式,來定義委託。

因此,我們對其限定名稱空間,限定其使用範圍:

同時將委託定義在某個類當成員變量:

通過定義事件,是使用委託的方式之一。

8、定義事件:

//定義事件
EventBuilder eb = tbClass.DefineEvent("MyEvent", EventAttributes.None, delegateBuilder);

MethodBuilder addMethod = tbClass.DefineMethod("add_OnAuth", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName, typeof(void), new Type[] { delegateBuilder });
ILGenerator addMethodIL = addMethod.GetILGenerator();
//......
addMethodIL.Emit(OpCodes.Ret);
eb.SetAddOnMethod(addMethod);

MethodBuilder removeMethod = tbClass.DefineMethod("remove_OnAuth", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName, typeof(void), new Type[] { delegateBuilder });
ILGenerator removeMethodIL = removeMethod.GetILGenerator();
//......
removeMethodIL.Emit(OpCodes.Ret);
eb.SetRemoveOnMethod(removeMethod);

注意事項:

1、定義事件,通過特殊方法: DefineEvent 來定義。

2、定義事件,第三個事件參數Type,需要傳遞 delegateBuilder ,則不是 delegateType,否則會報錯:

3、定義事件,需要同時掛兩個對應的添加和移除方法,否則,運行正常,但反編繹會報錯:

4、定義方法,傳遞的委託類型,和注意事項2一致,需要傳遞 delegateBuilder,否則一樣的錯誤信息。

下面查看正常情況下的反繹繹生成代碼:

對委託和事件的定義,一個神奇的Bug: 

通過反編繹 ILSpy 軟件,可以看到已經定義成功了,但通過引用生成的程序集,即發現裏面沒有相關的委託或事件產生?

同時通過 VS2022 自帶的反編繹【直接F12跳轉】,裏面也沒有任何相關的委託或事件代碼?

總結

構建類型是動態代碼生成過程中的關鍵一環,通過靈活運用 TypeBuilder 和相關工具,

我們可以實現各種複雜類型的動態生成,爲程序的靈活性和可擴展性提供有力支持。

總的來說,本章節通過演示如何使用 Emit 來動態創建類型,包括定義字段、方法、屬性和事件等,

幫助讀者理解如何在運行時生成和操作類型信息。

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