天天看點

委托和事件:

委托和事件:

1. 委托:一個能夠表示方法的資料類型;它将方法作為對象封裝起來,允許在運作時間接地綁定一個方法調用。

2. 聲明委托資料類型:

public delegate  bool GreaterThanHandler(int first , int second);

3. 委托的執行個體化:

為了執行個體化委托,需要和委托類型自身的簽名對應的一個方法;執行個體時不必用new來執行個體化該類的執行個體,直接傳遞名稱即可[C>.0新文法]。 如:

GreaterThanHandler a = 方法名;

C>.0以前的文法:

GreaterThanHandler a = new GreaterThanHandler (方法名) ;

4. 匿名方法:

匿名方法沒有實際方法聲明的委托執行個體,它們的定義是直接内嵌在代碼中的。如:

GreaterThanHandler a = delegate(int first , int second){return (first<second);};

5. 委托的内部機制:

C#将所有委托定義成間接派生于System.Delegate ,這個類有兩個屬性:(1)MethodInfo(System.Reflection.MethodInfo類型): 定義了一個特定方法簽名(包括方法的名稱、參數和傳回類型) (2)Target(Object類型):對象執行個體,其中包含了要調用的方法。

6. multicast委托:

一個委托變量可以引用一系列委托,在這一系列委托中,每個委托都順序指向一個後續的委托,進而形成一個委托鍊, 或者稱為multicast委托。

Publish-subscribe(釋出-訂閱)模式: 它對應這樣一種情形:需要将單一的事件通知(比如對象狀态發生的一個變化)廣播給多個訂閱者(subscriber).

7. 使用委托來編寫Observer模式(publish-subscribe模式):

例: 一個加熱器(Heater)和一個冷卻器(Cooler)連接配接到同一個溫度計(thermostat) 上。為了控制加熱器和冷卻器的打開和關閉,要向它們通知溫度的變化,溫度計将溫度的變化釋出(publish)給多個訂閱者-也就是加熱器和冷卻器。

看一段代碼:〔注意①②③④個步驟:〕

using System;

namespace test

{

    //定義訂閱者

    class Cooler //冷卻器

    {

        private float _Temperature;       //啟動裝置所需的溫度

        public float Temperature          //屬性

        {

            get { return _Temperature; }

            set { _Temperature = value; }

        }

        public Cooler(float temperature) //構造器

            Temperature = temperature;

        //③在事件訂閱者中定義事件處理程式

        public void OnTemperatureChanged(float newTemperature)    //訂閱者方法

            if (newTemperature > Temperature)

            {

                Console.WriteLine("Cooler:On");

            }

            else

                Console.WriteLine("Cooler:Off");

    }

    class Heater //加熱器

        public Heater(float temperature) //構造器

        public void OnTemperatureChanged(float newTemperature)      //訂閱者方法

            if (newTemperature < Temperature)

                Console.WriteLine("Heater:On");

                Console.WriteLine("Heater:Off");

    //定義釋出者

    class Thermostat

        public delegate void TemperatureChangeHandler(float newTemperature);   //定義委托資料類型,注意這是一個嵌套類;

//①在事件發行者中定義一個事件

        private TemperatureChangeHandler _OnTemperatureChange;   //存儲訂閱者清單,隻需一個委托字段即可存儲所有訂閱者(委托鍊)。

        public TemperatureChangeHandler OnTemperatureChange

            get { return _OnTemperatureChange; }

            set { _OnTemperatureChange = value;}

        }

        //設定由溫度計報告的目前溫度值并觸發事件

        private float _CurrentTemperature;

        public float CurrentTemperature

            get { return _CurrentTemperature; }

            set

                if (value != CurrentTemperature)

                {

                    _CurrentTemperature = value;

                    //②在事件發行者中觸發事件

                    TemperatureChangeHandler localOnChange = OnTemperatureChange;

                    if (localOnChange != null)    //調用一個委托之前,要檢查它的值是不是空值。

                    {

                        localOnChange(value);  //觸發事件

                    }

                }

    //連接配接釋出者和訂閱者

    class Program

        static void Main(string[] args)

        {

            Thermostat thermostat = new Thermostat();

            Heater heater = new Heater(60);

            Cooler cooler = new Cooler(80);

            string temperature;

            //④向事件發行者訂閱一個事件

            thermostat.OnTemperatureChange += heater.OnTemperatureChanged;   //向OnTemperatureChange注冊訂閱者; 

            thermostat.OnTemperatureChange += cooler.OnTemperatureChanged;

            Console.WriteLine("輸入溫度:");

            temperature = Console.ReadLine();

            thermostat.CurrentTemperature = int.Parse(temperature);

            Console.ReadLine();

}

8. 委托運算符:

+= , -= ; + , - ;

注:使用指派運算符,會清除之前的所有訂閱者,并允許使用新的訂閱者替換它們。

9. multicast委托的内部機制:

->delegate關鍵字是派生自System.MulticastDelegate的一個類型的别名;MulticastDelegate類包含一個對象引用和一個方法指針。當向一個multicast委托添加一個方法時,MulticastDelegate類會建立委托類型的一個新執行個體,在新執行個體中為新增的方法存儲對象引用和方法指針,并在委托執行個體清單中添加新的委托執行個體作為下一項。MulticastDelegate類維護着由多個Delegate對象構成的一個連結清單。

但是有兩個問題需要解決:

1)      錯誤處理:假如一個訂閱者引發了一個異常,鍊中的後續訂閱者就接收不到通知;

2)      方法傳回值和傳引用:因為調用一個委托,就有可能造成将一個通知發送給多個訂閱者,假如訂閱者會傳回值,就不确定到底該使用哪個訂閱者的傳回值。

以上兩個問題都可以用GetInvocationList()方法周遊每個委托調用清單來處理。

10. 事件:

事件的目的:

1)      event關鍵字的目的就是提供額外的封裝,避免你不小心地以取消其他訂閱者;

2)      事件確定隻有包容類才能觸發一個事件通知;

總言之:event關鍵字提供了必要的封裝來防止任何外部類釋出一個事件或取消之前的訂閱者。

下面這段代碼對上述代碼進行了修改:

using System.Collections.Generic;

using System.Linq;

using System.Text;

        public float Temperature          //屬性

        public void OnTemperatureChanged(object sender, Thermostat.TemperatureArgs newTemperature)    //訂閱者方法

            if (newTemperature.NewTemperature > Temperature)

                Console.WriteLine("Cooler:On");

            else

            get { return _Temperature; }

        public void OnTemperatureChanged(object sender,Thermostat .TemperatureArgs newTemperature)      //訂閱者方法

            if (newTemperature.NewTemperature < Temperature)

                Console.WriteLine("Heater:Off");

        public class TemperatureArgs : System.EventArgs

            public TemperatureArgs(float newTemperature)

                NewTemperature = newTemperature;

            public float NewTemperature

                get { return _newTemperature; }

                set { _newTemperature = value; }

            private float _newTemperature;

        //①在事件發行者中定義一個事件

        public delegate void TemperatureChangeHandler(object sender, TemperatureArgs newTemperature);   //定義委托資料類型,注意這是一個嵌套類;

        public event TemperatureChangeHandler OnTemperatureChange;

        //public TemperatureChangeHandler OnTemperatureChange         //存儲訂閱者清單,隻需一個委托字段即可存儲所有訂閱者(委托鍊)。

        //{

        //    get { return _OnTemperatureChange; }

        //    set { _OnTemperatureChange = value; }

        //}

        //private TemperatureChangeHandler _OnTemperatureChange;

                    if (OnTemperatureChange != null)

                        OnTemperatureChange(this, new TemperatureArgs(value));

                    }

            //thermostat.OnTemperatureChange(44);

作者:​​水木​​    

繼續閱讀