C#委託和事件

delegate是C#中的一種類型,它實際上是一個能夠持有對某個方法的引用的類。與其它的類不同,delegate類能夠擁有一個簽名(signature),並且它”只能持有與它的簽名相匹配的方法的引用“。它所實現的功能與C/C++中的函數指針十分相似。它允許你傳遞一個類A的方法m給另一個類B的對象,使得類B的對象能夠調用這個方法m。但與函數指針相比,delegate有許多函數委託和事件在 .Net Framework中的應用非常廣泛指針不具備的優點。首先,函數指針只能指向靜態函數,而delegate既可以引用靜態函數,又可以引用非靜態成員函數。在引用非靜態成員函數時,delegate不但保存了對此函數入口指針的引用,而且還保存了調用此函數的類實例的引用。其次,與函數指針相比,delegate是面向對象、類型安全、可靠的受控(managed)對象。也就是說,runtime能夠保證delegate指向一個有效的方法,你無須擔心delegate會指向無效地址或者越界地址。
實現一個delegate是很簡單的,通過以下3個步驟即可實現一個delegate:
1、聲明一個delegate對象,它應當與你想要傳遞的方法具有相同的參數和返回值類型。
2、創建delegate對象,並”將你想要傳遞的函數作爲參數傳入”。
3、在要實現異步調用的地方,通過上一步創建的對象來調用方法。
 

using System;

public class MyDelegateTest
{
        // 步驟1,聲明delegate對象
        public delegate void MyDelegate(string name);

        // 這是我們欲傳遞的方法,它與MyDelegate具有相同的參數和返回值類型
        public static void MyDelegateFunc(string name)
        {
                  Console.WriteLine("Hello, ", name);
        }
        public static void Main()
        {
            // 步驟2,創建delegate對象(實例??)
            MyDelegate md = new MyDelegate(MyDelegateTest.MyDelegateFunc);
            // 步驟3,調用delegate
            md("sam1111");
        }
}

輸出結果是:Hello, sam1111
瞭解了delegate,下面我們來看看,在C#中對事件是如何處理的。
C#中的事件處理實際上是一種具有特殊簽名的delegate,象下面這個樣子:
public delegate void CalculateEventHandler(object sender, MyEventArgs e);
其中的兩個參數,sender代表事件發送者,e是事件參數類。MyEventArgs類用來包含與事件相關的數據,所有的事件參數類都必須從System.EventArgs類派生。當然,如果你的事件不含參數,那麼可以直接用System.EventArgs類作爲參數。
就是這麼簡單,結合delegate的實現,我們可以將自定義事件的實現歸結爲以下幾步:
1、定義delegate對象類型,它有兩個參數,第一個參數是事件發送者對象,第二個參數是事件參數類對象。
2、定義事件參數類,此類應當從System.EventArgs類派生。如果事件不帶參數,這一步可以省略。
3、定義”事件處理方法,它應當與delegate對象具有相同的參數和返回值類型”。
4、用event關鍵字定義事件對象,它同時也是一個delegate對象。
5、用+=操作符添加事件到事件隊列中(-=操作符能夠將事件從隊列中刪除)。
6、在需要觸發事件的地方用調用delegate的方式寫事件觸發方法。一般來說,此方法應爲protected訪問限制,既不能以public方式調用,但可以被子類繼承。名字是OnEventName。
7、在適當的地方調用事件觸發方法觸發事件。
 

public class Calculator
{
        /// <summary>
        /// 定義一個CalculateEventArgs
        /// 用於存放事件引發時向處理程序傳遞的狀態信息
        /// </summary>
        public class CalculateEventArgs : EventArgs
        {

            public readonly int x, y;

            public CalculateEventArgs(int x, int y)
            {
                this.x = x;
                this.y = y;
            }
        }

        //聲明事件委託
        public delegate void CalculateEventHandler(object sender, CalculateEventArgs e);

        //定義事件成員,提供外部綁定
        public event CalculateEventHandler MyCalculate;


        //提供受保護的虛方法,可以由子類覆寫來拒絕監視
        public virtual void OnCalculate(CalculateEventArgs e)
        {
            if (MyCalculate != null)
            {
                MyCalculate(this, e);
            }

        }

        //進行計算,調用該方法表示有新的計算髮生
        public void Calculate(int x, int y)
        {
            CalculateEventArgs e = new CalculateEventArgs(x, y);
            OnCalculate(e);
        }
}

一件事件的完整程序就這樣定義好了,然後需要一個事件觸發程序,用來監聽事件:

    /// <summary>
    /// 定義事件觸發者
    /// </summary>
    public class CalculateManager
    {
        /// <summary>
        /// 定義信息通知方法
        /// </summary>
        public void Add(object sender, Calculator.CalculateEventArgs e)
        {
            Console.WriteLine(e.x + "==========" + e.y);
        }
        public void Substract(object sender, Calculator.CalculateEventArgs e)
        {
            Console.WriteLine(e.x + "==========" + e.y);
        }

    }

之後就是實現事件的處理

/// <summary>
    /// 
    /// </summary>
    public class Test_Calculator
    {
        public static void Main11()
        {
            Calculator calculator = new Calculator();
            //事件觸發者
            CalculateManager cm = new CalculateManager();

            //事件綁定
            calculator.MyCalculate += cm.Add;
            calculator.Calculate(100, 200);

            calculator.MyCalculate += cm.Substract;
            calculator.Calculate(100, 200);

            //事件註銷
            calculator.MyCalculate -= cm.Add;
            calculator.Calculate(100, 200);

        }
    }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    class Program
    {
        public class Heater
        {
            private int temperature;
            public string type = "RealFire 001";       // 添加型號作爲演示
            public string area = "China Xian";         // 添加產地作爲演示
            //聲明委託
            public delegate void BoiledEventHandler(Object sender, BoiledEventArgs e);
            public event BoiledEventHandler Boiled; //聲明事件

            // 定義BoiledEventArgs類,傳遞給Observer所感興趣的信息
            public class BoiledEventArgs : EventArgs
            {
                public readonly int temperature;
                public BoiledEventArgs(int temperature)
                {
                    this.temperature = temperature;
                }
            }

            // 可以供繼承自 Heater 的類重寫,以便繼承類拒絕其他對象對它的監視
            protected virtual void OnBoiled(BoiledEventArgs e)
            {
                if (Boiled != null)
                { // 如果有對象註冊
                    Boiled(this, e);  // 調用所有註冊對象的方法
                }
            }

            // 燒水。
            public void BoilWater()
            {
                for (int i = 0; i <= 100; i++)
                {
                    temperature = i;
                    if (temperature > 95)
                    {
                        //建立BoiledEventArgs 對象。
                        BoiledEventArgs e = new BoiledEventArgs(temperature);
                        OnBoiled(e);  // 調用 OnBolied方法
                    }
                }
            }
        }

        // 警報器
        public class Alarm
        {
            public void MakeAlert(Object sender, Heater.BoiledEventArgs e)
            {
                Heater heater = (Heater)sender;     //這裏是不是很熟悉呢?
                //訪問 sender 中的公共字段
                Console.WriteLine("Alarm:{0} - {1}: ", heater.area, heater.type);
                Console.WriteLine("Alarm: 嘀嘀嘀,水已經 {0} 度了:", e.temperature);
                Console.WriteLine();
            }
        }

        // 顯示器
        public class Display
        {
            public static void ShowMsg(Object sender, Heater.BoiledEventArgs e)
            {   //靜態方法
                Heater heater = (Heater)sender;
                Console.WriteLine("Display:{0} - {1}: ", heater.area, heater.type);
                Console.WriteLine("Display:水快燒開了,當前溫度:{0}度。", e.temperature);
                Console.WriteLine();
            }
        }
        static void Main(string[] args)
        {
            Heater heater = new Heater();
            Alarm alarm = new Alarm();

            heater.Boiled += alarm.MakeAlert;   //註冊方法
            //heater.Boiled += (new Alarm()).MakeAlert;      //給匿名對象註冊方法
            //heater.Boiled += new Heater.BoiledEventHandler(alarm.MakeAlert);    //也可以這麼註冊
            heater.Boiled += Display.ShowMsg;       //註冊靜態方法

            heater.BoilWater();   //燒水,會自動調用註冊過對象的方法

        }
    }
}

 

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