程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Emit學習-進階篇-定義事件

Emit學習-進階篇-定義事件

編輯:關於.NET

之前在研究如何用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

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved