之前在研究如何用Emit為動態類添加事件,本來以為會非常簡單,但是卻碰到 了許多的問題,有些問題在之前的答疑篇中已經提到了,並予以了解決,雖然有 些地方自己也不是很明白,但畢竟還是解決了,最後比較我寫的IL代碼,和系統 自動生成的,總有一些地方無法做到一致。特別是在為事件添加add和remove方法 時,碰到了許多問題,下面我將針對這些問題進行講解。按照慣例,先給出要實 現的類的C#代碼,方便反編譯後對照著進行IL代碼的書寫,代碼如下:
Publisher
public class Publisher { private bool isStart = false; private Random random = new Random(DateTime.Now.Millisecond); public void Start() { if (!isStart) { isStart = true; GenerateRand(); } } public void Stop() { isStart = false; } private void GenerateRand() { while (isStart) { OnRandGenerated(random.Next(10000)); Thread.Sleep(1000); } } #region Event public event EventHandler<RandGeneratedEventArgs> RandGenerated; protected virtual void OnRandGenerated(int rand) { RaiseRandGeneratedEvent(rand); } private void RaiseRandGeneratedEvent(int rand) { EventHandler<RandGeneratedEventArgs> temp = RandGenerated; if (temp != null) { RandGeneratedEventArgs arg = new RandGeneratedEventArgs(rand); temp(this, arg); } } #endregion }
首先,我們定義類中的相關字段及方法,這部分使用之前基礎篇中提到過的步 驟就可以完成,不再累述,但是要指出的一點是,在類中,以如下形式初始化的 字段:private Random random = new Random(DateTime.Now.Millisecond);如果 初始化的值不是常量,則需要在無參構造函數中對齊進行初始化,否則可以使用 FieldBuilder的SetConstant方法進行初始化。
在定義事件時,發現需要手動添加事件的add和remove方法,在這裡面會用到 事件的本身,而OpCodes指令中好像也沒有加載事件的指令,只有加載字段的指令 ,然後仔細研究了自動生成的IL代碼,發現其中生成了一個與定義的事件的同名 的私有字段,然後相關的訪問都是通過對這個字段的訪問來進行的,於是豁然開 朗,看來Reflector果然是個好東西呀!
當定義事件的add和remove方法時,需要注意,我們平時在C#中使用的都是形 如+=、-=這樣的形式,但是我用這種方法後發現行不通,最後又是在Reflector的 幫助下,發現應該分別使用Delegate的Combine和Remove方法,廢話不多說,代碼 和相關注釋如下:
Event
#region Event //定義事件,事件的可訪問性由和它相關的getset方法決定 EventBuilder randGeneratedEvent = publisherTypeBuilder.DefineEvent ("RandGenerated", EventAttributes.None, eventType); MethodBuilder addEventBuilder = publisherTypeBuilder.DefineMethod ("add_RandGenerated", MethodAttributes.Public, null, new Type[] { eventType }); //注意在我們使用事件時,使用的是+=操作,但在IL代碼中並不是如此,應該使 用Delegate.Combine方法 ILGenerator addEventIL = addEventBuilder.GetILGenerator(); addEventIL.Emit(OpCodes.Ldarg_0); addEventIL.Emit(OpCodes.Ldarg_0); addEventIL.Emit(OpCodes.Ldfld, randGeneratedField); addEventIL.Emit(OpCodes.Ldarg_1); addEventIL.Emit(OpCodes.Call, typeof(Delegate).GetMethod("Combine", new Type[] { eventType, eventType })); //返回的是Delegate類型,所以需要進行轉換 addEventIL.Emit(OpCodes.Castclass, eventType); addEventIL.Emit(OpCodes.Stfld, randGeneratedField); randGeneratedEvent.SetAddOnMethod(addEventBuilder); MethodBuilder removeEventBuilder = publisherTypeBuilder.DefineMethod ("remove_RandGenerated", MethodAttributes.Public, null, new Type[] { eventType }); //注意在我們使用事件時,使用的是-=操作,但在IL代碼中並不是如此,應該使 用Delegate.Remove方法 ILGenerator removeEventIL = removeEventBuilder.GetILGenerator(); removeEventIL.Emit(OpCodes.Ldarg_0); removeEventIL.Emit(OpCodes.Ldarg_0); removeEventIL.Emit(OpCodes.Ldfld, randGeneratedField); removeEventIL.Emit(OpCodes.Ldarg_1); removeEventIL.Emit(OpCodes.Call, typeof(Delegate).GetMethod("Remove", new Type[] { eventType, eventType })); //返回的是Delegate類型,所以需要進行轉換 removeEventIL.Emit(OpCodes.Castclass, eventType); removeEventIL.Emit(OpCodes.Stfld, randGeneratedField); randGeneratedEvent.SetRemoveOnMethod(removeEventBuilder); #endregion
本文配套源碼