前言:
在動態生成代碼的過程中,構建類型(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 來動態創建類型,包括定義字段、方法、屬性和事件等,
幫助讀者理解如何在運行時生成和操作類型信息。