原文地址:http://www.cnblogs.com/MythYsJh/archive/2010/04/28/1723398.html
本文講述用Emit創建枚舉,結構體,類,接口等類型.
同樣,讓我們先搭好大體框架:
assemName.Name = "EmitStudy3";
AssemblyBuilder asmBuilder =
AppDomain.CurrentDomain.DefineDynamicAssembly(assemName,
AssemblyBuilderAccess.RunAndSave);
var mdlBldr = asmBuilder.DefineDynamicModule("EmitStudy3", "EmitStudy3.dll");
//TODO:添加自定義類型
asmBuilder.Save("EmitStudy3.dll");
一、創建枚舉
枚舉通過ModuleBuilder.DefineEnum來實現。
這裏定義了一個基礎類型爲int,名稱爲Sex的public枚舉。下面,讓我們再爲該枚舉添加'內容':
enumBldr.DefineLiteral("Female", 1);
之後也需要像創建類一樣使用
來完成一個枚舉的創建。這是用Reflector查看就可以看到如下一個枚舉了:
{
Male,
Female
}
二、結構體(struct)
在ModuleBuilder中並不存在DefineStruct之類的方法。那該如何創建一個結構體呢?我們首先用C#創建一個結構體然後反編譯成IL就會發現結構體實質是繼承於ValueType的一個類。因此我們可以這樣來創建一個結構體:
var fldFirstName = nameStruct.DefineField("FirstName", typeof(string), FieldAttributes.Private);
var fldLastName = nameStruct.DefineField("LastName", typeof(string), FieldAttributes.Private);
var methodGetFirstName = nameStruct.DefineMethod("GetFirstName", MethodAttributes.Public, CallingConventions.Standard, typeof(string), null);
var methodSetFirstName = nameStruct.DefineMethod("SetFirstName", MethodAttributes.Public, CallingConventions.Standard, null,new []{ typeof(string)});
var ilGetFirstName = methodGetFirstName.GetILGenerator();
ilGetFirstName.Emit(OpCodes.Ldarg_0);
ilGetFirstName.Emit(OpCodes.Ldfld, fldFirstName);
ilGetFirstName.Emit(OpCodes.Ret);
var ilSetFirstName = methodSetFirstName.GetILGenerator();
ilSetFirstName.Emit(OpCodes.Ldarg_0);
ilSetFirstName.Emit(OpCodes.Ldarg_1);
ilSetFirstName.Emit(OpCodes.Stfld, fldFirstName);
ilSetFirstName.Emit(OpCodes.Ret);
var prptFirstName = nameStruct.DefineProperty("FirstName", PropertyAttributes.None, typeof(string), null);
prptFirstName.SetGetMethod(methodGetFirstName);
prptFirstName.SetSetMethod(methodSetFirstName);
var methodGetLastName = nameStruct.DefineMethod("GetLastName", MethodAttributes.Public, CallingConventions.Standard, typeof(string), null);
var methodSetLastName = nameStruct.DefineMethod("SetLastName", MethodAttributes.Public, CallingConventions.Standard, null, new[] { typeof(string) });
var ilGetLastName = methodGetLastName.GetILGenerator();
ilGetLastName.Emit(OpCodes.Ldarg_0);
ilGetLastName.Emit(OpCodes.Ldfld, fldLastName);
ilGetLastName.Emit(OpCodes.Ret);
var ilSetLastName = methodSetLastName.GetILGenerator();
ilSetLastName.Emit(OpCodes.Ldarg_0);
ilSetLastName.Emit(OpCodes.Ldarg_1);
ilSetLastName.Emit(OpCodes.Stfld, fldLastName);
ilSetLastName.Emit(OpCodes.Ret);
var prptLastName = nameStruct.DefineProperty("LastName", PropertyAttributes.None, typeof(string), null);
prptLastName.SetGetMethod(methodGetLastName);
prptLastName.SetSetMethod(methodSetLastName);
nameStruct.CreateType();
上面就創建了一個包含FirstName和LastName的結構體Name。
三、創建接口
同樣在ModuleBuilder中也沒有DefineInterface這樣的方法。而通過觀察IL代碼同樣可以發現接口其實也是類。因此我們還是可以想創建類一樣來創建接口.
iStudentBldr.DefineMethod("Update", MethodAttributes.Virtual | MethodAttributes.Abstract, null, null);
iStudentBldr.CreateType();
這樣就創建了一個僅包含一個Update方法的接口IStudent。在創建接口時也要注意TypeAttributes需聲明爲 TypeAttributes.Interface | TypeAttributes.Abstract,而接口包含的方法的MethodAttributes則必須爲MethodAttributes.Virtual | MethodAttributes.Abstract。
四、創建類
已經有很多這方面的例子,這裏就不再贅述。
五、委託和事件
老規矩,從反編譯的IL代碼入手。可以發現我們創建的委託其實是派生自MultiDelegate類的類。而MultiDelegate這個類比較特殊,它不能從C#代碼裏被繼承,但在IL代碼卻可以。因此可以如下來定義委託。
delPropertyChangedHandler.CreateType();
但是這樣的定義似乎總覺得很奇怪,因爲委託還包含返回值及參數等信息,這些在這段代碼都沒有得到體現。那麼該如何創建委託呢?讓我們還是從現有的入手,以EventHandler爲例,在Reflector下它的結構爲
事實上,創建委託必須包含一個構造函數和Invoke方法。Invoke方法的參數則是委託的參數,返回類型即爲委託的返回類型。
var constructor = delPropertyChangedHandler.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new[] { typeof(object), typeof(IntPtr) });
constructor.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
var methodInvoke = delPropertyChangedHandler.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.Virtual, null, new[] { typeof(string) });
methodInvoke.SetImplementationFlags(MethodImplAttributes.Managed | MethodImplAttributes.Runtime);
var delType = delPropertyChangedHandler.CreateType();
構造函數和Invoke方法都使用了SetImplementationFlags(MethodImplAttributes.Managed | MethodImplAttributes.Runtime),因爲二者的方法體是在運行時才能決定的。
既然已經創建了委託,讓我們再用此委託添加一個事件。定義事件就相對容易些了,直接使用TypeBuilder.DefineEvent即可.
//其餘部分
typeBldr.DefineEvent("OnNameChanged", EventAttributes.None, delType);
typeBldr.CreateType();
六、小結
我們已經可以用Emit創建常見元素了。當然,Emit還可以創建很多其他內容,不可能介紹完全,關鍵是要掌握通過C#和IL對比來完成Emit的基本方法。這樣,我們就基本可以解決遇到的大部分問題了。歡迎大家指正。