天天看點

C#中的事件與委托

##Delegate委托

delegate是C#中的一種類型,它實際上是一個能夠持有對某個方法的引用的類。與其它的類不同,delegate類能夠擁有一個簽名(signature),并且它"隻能持有與它的簽名相比對的方法的引用"。

它所實作的功能與C/C++中的函數指針十分相似。它允許你傳遞一個類A的方法m給另一個類B的對象,使得類B的對象能夠調用這個方法m。但與函數指針相比,delegate有許多函數委托和事件在 .Net Framework中的應用非常廣泛指針不具備的優點。首先,函數指針隻能指向靜态函數,而delegate既可以引用靜态函數,又可以引用非靜态成員函數。在引用非靜态成員函數時,delegate不但儲存了對此函數入口指針的引用,而且還儲存了調用此函數的類執行個體的引用。其次,與函數指針相比,delegate是面向對象、類型安全、可靠的受控(managed)對象。也就是說,runtime能夠保證delegate指向一個有效的方法,你無須擔心delegate會指向無效位址或者越界位址。

##結合delegate的實作,将自定義事件的實作歸結為以下幾步:

1.定義delegate對象類型,它有兩個參數,第一個參數是事件發送者對象,第二個參數是事件參數類對象。它應當與你想要傳遞的方法具有相同的參數和傳回值類型。

2.定義事件參數類,此類應當從System.EventArgs類派生。如果事件不帶參數,這一步可以省略。

3.定義"事件處理方法,它應當與delegate對象具有相同的參數和傳回值類型"。

4.用event關鍵字定義事件對象,它同時也是一個delegate對象。

5.用+=操作符添加事件到事件隊列中(-=操作符能夠将事件從隊列中删除)。

6.在需要觸發事件的地方用調用delegate的方式寫事件觸發方法。一般來說,此方法應為protected通路限制,既不能以public方式調用,但可以被子類繼承。名字是OnEventName。

7. 在适當的地方調用事件觸發方法觸發事件。

####舉例說明:

public class EventTest
{
       // 步驟1,定義delegate對象,聲明事件的委托
       public delegate void MyEventHandler(object sender, System.EventArgs e);
       
       // 步驟2(定義事件參數類)省略
       public class MyEventCls
       {
       // 步驟3,定義事件處理方法,它與delegate對象具有相同的參數和傳回值類型
                public  void MyEventFunc(object sender, System.EventArgs e)
                {
                           Console.WriteLine("My event is ok!");
                }
       }
       // 步驟4,用event關鍵字定義事件對象(聲明事件)
       private event MyEventHandler myevent;
       private MyEventCls myecls;
      public EventTest()
      {
                myecls = new MyEventCls(); 
                
       // 步驟5,用+=操作符将事件添加到隊列中(用-=操作符移除隊列的事件)
                this.myevent += new MyEventHandler(myecls.MyEventFunc);
      } 
      
      // 步驟6,以調用delegate的方式寫事件觸發函數
     protected void OnMyEvent(System.EventArgs e)
      {
               if(myevent != null)
                       myevent(this, e);
      }
     public void RaiseEvent()
      {
               EventArgs e = new EventArgs(); 
               
       // 步驟7,觸發事件
               OnMyEvent(e);
      }
      public static void Main()
      {
               EventTest et = new EventTest();
               Console.Write("Please input ''a'':");
               string s = Console.ReadLine();
               if(s == "a")
               {
                     et.RaiseEvent();
               }
               else
              {
                        Console.WriteLine("Error");
              }
      }
}
                

##總結:

1.委托是一個類,它定義了方法的類型,使得可以将方法當作另一個方法的參數來進行傳遞,這種将方法動态地賦給參數的做法,可以避免在程式中大量使用If-Else(Switch)語句,同時使得程式具有更好的可擴充性。

2.使用委托可以将多個方法綁定到同一個委托變量,當調用此變量時(這裡用“調用”這個詞,是因為此變量代表一個方法),可以依次調用所有綁定的方法。

3.在使用委托的時候,你可以像對待一個類一樣對待它。即先聲明,再執行個體化。隻是有點不同,類在執行個體化之後叫對象或執行個體,但委托在執行個體化後仍叫委托。

4.事件應該由事件釋出者觸發,而不應該由用戶端(客戶程式)來觸發。

##最後以《鴻門宴》中的一個虛拟情節來體會委托的用法:

鴻門宴之前,項羽和手下的範增和項莊商量好了,等他和劉邦喝酒的時候,項羽左手舉起酒杯,範增就出兵刺殺劉邦;項羽右手舉起酒杯,項莊就出兵刺殺劉邦;如果項羽摔碎了酒杯,範增和項莊就一起出兵刺殺劉邦。

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

namespace 委托
{
    class 鴻門宴
    {
       
        /// <summary>
        /// 定義項羽舉杯的委托
        /// </summary>
        /// <param name="hand">手:左、右</param>
        public delegate void RaiseEventHandler(string hand); 
        
        /// <summary>
        /// 定義項羽摔碎酒杯的委托
        /// </summary>
        public delegate void FallEventHandler(); 
        
        /// <summary>
        /// 定義項羽
        /// </summary>
        public class XiangYu
        {
            /// <summary>
            /// 項羽舉杯事件
            /// </summary>
            public event RaiseEventHandler RaiseEvent; 
            
            /// <summary>
            /// 項羽摔杯事件
            /// </summary>
            public event FallEventHandler FallEvent;

            /// <summary>
            /// 定義項羽舉杯事件的觸發函數
            /// </summary>
            /// <param name="hand">手:左、右</param>
            public void Raise(string hand)
            {
                Console.WriteLine("項羽{0}手舉杯", hand);

                // 調用舉杯事件,傳入左或右手作為參數
                if (RaiseEvent != null)
                {
                    RaiseEvent(hand);
                }
            } 
            
            /// <summary>
            /// 定義項羽摔碎酒杯事件的觸發函數
            /// </summary>
            public void Fall()
            {
                Console.WriteLine("項羽摔碎酒杯");
                // 調用摔杯事件
                if (FallEvent != null)
                {
                    FallEvent();
                }
            }
        } 
        
        /// <summary>
        /// 定義項羽部下範增
        /// </summary>
        public class FanZeng
        {

            XiangYu xiangyu;

            public FanZeng(XiangYu xiangyu)
            {
                this.xiangyu = xiangyu;
                // 訂閱項羽的舉杯事件
                xiangyu.RaiseEvent += new RaiseEventHandler(xiangyu_RaiseEvent);
                // 訂閱項羽的摔杯事件
                xiangyu.FallEvent += new FallEventHandler(xiangyu_FallEvent); 
            }
            /// <summary>
            /// 範增對項羽舉杯事件的響應
            /// </summary>
            /// <param name="hand">若項羽左手舉杯,則範增發起攻擊</param>
            void xiangyu_RaiseEvent(string hand)
            {
                if (hand.Equals("左"))
                {
                    Attack();
                }
            }

            /// <summary>
            /// 範增對項羽摔碎酒杯事件的響應
            /// </summary>
            void xiangyu_FallEvent()
            {
                Attack();
            }

            /// <summary>
            /// 定義攻擊的函數
            /// </summary>
            public void Attack()
            {
                Console.WriteLine("範增發起攻擊,大喊:劉邦拿命來!");
            }
        }
        
        /// <summary>
        /// 部下項莊
        /// </summary>
        public class XiangZhuang
        {
            XiangYu xiangyu;
            public XiangZhuang(XiangYu xiangyu)
            {
                this.xiangyu = xiangyu;
                // 訂閱舉杯事件
                xiangyu.RaiseEvent += new RaiseEventHandler(xiangyu_RaiseEvent);
                // 訂閱摔杯事件 
                xiangyu.FallEvent += new FallEventHandler(xiangyu_FallEvent); 
            } 
            
            /// <summary>
            /// 項莊對項羽舉杯事件的響應
            /// </summary>
            /// <param name="hand">若項羽右手舉杯,則攻擊</param>
            void xiangyu_RaiseEvent(string hand)
            {
                if (hand.Equals("右"))
                {
                    Attack();
                }
            }

            /// <summary>
            /// 項莊對項羽摔碎酒杯事件的響應
            /// </summary>
            void xiangyu_FallEvent()
            {
                Attack();
            }
            /// <summary>
            /// 定義攻擊的函數
            /// </summary>
            public void Attack()
            {
                Console.WriteLine("項莊發起攻擊,一劍刺向劉邦!");
            }
        }
        
		   //主函數
         static void Main(string[] args)
        {
            XiangYu xiangyu = new XiangYu(); // 執行個體化項羽

            FanZeng fanzeng = new FanZeng(xiangyu); // 執行個體化部下範增

            XiangZhuang xiangzhuang = new XiangZhuang(xiangyu); // 執行個體化部下項莊

            // 宴會上項羽左手舉酒杯
            xiangyu.Raise("左");

            // 宴會上項羽右手舉酒杯
            xiangyu.Raise("右");

            // 宴會上項羽摔碎了酒杯
            xiangyu.Fall();

            Console.ReadLine();

            // 由于範增和項莊都訂閱了項羽的事件,是以無需任何代碼,範增和項莊均會按照約定進行操作。
        }


    }
}

                
C#中的事件與委托