概述
C#中的委托類似於C或C++中的函數指針。使用委托使程序員可以將方法引用封裝在委托對象內。然後可以將該委托對象傳遞給可調用所引用方法的代碼,而不必在編譯時知道將調用哪個方法。與C或C++中的函數指針不同,委托是面向對象,而且是類型安全的。
C#中的“事件”是當對象發生某些事情時,類向該類的客戶提供通知的一種方法。事件最常見的用途是用於圖形用戶界面;通常,表示界面中的控件的類具有一些事件,當用戶對控件進行某些操作(如單擊某個按鈕)時,將通知這些事件。
使用委托來聲明事件。委托對象封裝一個方法,以便可以匿名調用該方法。事件是類允許客戶為其提供方法(事件發生時應調用這些方法)的委托的一種方法。事件發生時,將調用其客戶提供給它的委托。
注明:委托是對方法的包裝 在不確定要調用什麼方法時候而又不能用抽象或者多態實現的時候用委托。
- public interface PilotLamp
- {
- /// <summary>
- /// green light
- /// </summary>
- void TurnOn();
- /// <summary>
- /// notice
- /// </summary>
- string Notice
- {
- get;
- set;
- }
- }
先創建PilotLamp.cs
再創建DelegateEvent.cs:
- public delegate void EventHandler();
再創建TrafficLight.cs:
- public class TrafficLight : PilotLamp
- {
- public event EventHandler Notices;
- private string notice;
- #region GreenLight 成員
- public void TurnOn()
- {
- if (Notices != null)
- Notices();
- }
- public string Notice
- {
- get
- {
- return notice;
- }
- set
- {
- notice = value;
- }
- }
- #endregion
- }
再創建Driver.cs
- public class Driver
- private string Name;
- private PilotLamp greenLight;
- public Driver(string name, PilotLamp greenLight)
- {
- this.Name = name;
- this.greenLight = greenLight;
- }
- public void GoLeft()
- {
- Console.WriteLine(string.Format("{1}司機,{0},請向左開車.", greenLight.Notice, Name));
- }
- }
再創建Pedestrian.cs:
- public class Pedestrian
- {
- private string Name;
- private PilotLamp greenLight;
- public Pedestrian(string name, PilotLamp greenLight)
- {
- this.Name = name;
- this.greenLight = greenLight;
- }
- public void GoThrough()
- {
- Console.WriteLine( string.Format("{0}同志,{1},請向前走.", Name, greenLight.Notice));
- }
- }
最後再調用:
- public partial class Run : Form
- {
- public Run()
- {
- InitializeComponent();
- }
- private void btnRun_Click(object sender, EventArgs e)
- {
- //-------------------------------------
- TrafficLight trafficLight = new TrafficLight();
- Driver driverOne = new Driver("張三", trafficLight);
- Driver driverTwo = new Driver("李四", trafficLight);
- Pedestrian pedestrianOne = new Pedestrian("王五", trafficLight);
- Pedestrian pedestrianTwo = new Pedestrian("麻六", trafficLight);
- trafficLight.Notices += new Observer.EventHandler(driverOne.GoLeft);
- trafficLight.Notices += new Observer.EventHandler(driverTwo.GoLeft);
- trafficLight.Notices += new Observer.EventHandler(pedestrianOne.GoThrough);
- trafficLight.Notices += new Observer.EventHandler(pedestrianTwo.GoThrough);
- trafficLight.Notice = "綠燈亮了.";
- trafficLight.TurnOn();
- //-------------------------------------
- }
- }
輸出時選控制台應用程序如圖:
結果如下圖:
事件的使用示例:
- namespace DelegateAndEvent
- {
- class Program
- {
- static void Main(string[] args)
- {
- Publishser pub = new Publishser();
- OneScriber oneSub = new OneScriber();
- TwoScriber twoSub = new TwoScriber();
- ThreeScriber threeSub = new ThreeScriber ();
- pub.NumberChanged += new GeneralEventHandler(oneSub.OnNumberChanged);
- pub.NumberChanged += new GeneralEventHandler(twoSub.OnNumberChanged);
- pub.NumberChanged += new GeneralEventHandler(threeSub.OnNumberChanged);
- pub.DOSomething();
- }
- }
- public delegate string GeneralEventHandler();
- public class Publishser
- {
- public event GeneralEventHandler NumberChanged;
- public void DOSomething()
- {
- if (NumberChanged != null)
- {
- Delegate[] generalEventHandlers = NumberChanged.GetInvocationList();
- foreach (Delegate generalEventHandler in generalEventHandlers)
- {
- GeneralEventHandler mothed = (GeneralEventHandler)generalEventHandler;
- string rtn = mothed();
- Console.WriteLine(rtn);
- System.Threading.Thread.Sleep(2000);
- }
- }
- }
- }
- public class OneScriber
- {
- public string OnNumberChanged()
- {
- return "One Subscriber";
- }
- }
- public class TwoScriber
- {
- public string OnNumberChanged()
- {
- return "Two Subscriber";
- }
- }
- public class ThreeScriber
- {
- public string OnNumberChanged()
- {
- return "Three Subscriber";
- }
- }
- }
運行結果:
注意到Delegate是GeneralEventHandler
的基類,所以為了觸發事件,先要進行一個向下的強制轉換,之後才能在其上觸發事件,調用所有注冊對象的方法。除了使用這種方式以外,還有一種更靈活方式可以調用方法,它是定義在Delegate基類中的DynamicInvoke()方法:
- public object DynamicInvoke(params object[] args);
這可能是調用委托最通用的方法了,適用於所有類型的委托。它接受的參數為object[],也就是說它可以將任意數量的任意類型作為參數,並返回單個object對象。上面的DOSomething()方法也可以改寫成下面這種通用形式:
代碼作如下改動:
- namespace DelegateAndEvent
- {
- class Program
- {
- static void Main(string[] args)
- {
- Publishser pub = new Publishser();
- OneScriber oneSub = new OneScriber();
- TwoScriber twoSub = new TwoScriber();
- ThreeScriber threeSub = new ThreeScriber();
- pub.NumberChanged += new GeneralEventHandler(oneSub.OnNumberChanged);
- pub.NumberChanged += new GeneralEventHandler(twoSub.OnNumberChanged);
- pub.NumberChanged += new GeneralEventHandler(threeSub.OnNumberChanged);
- List<string> strlist = pub.DOSomething();
- foreach (string result in strlist)
- Console.WriteLine(result);
- System.Threading.Thread.Sleep(5000);
- }
- }
- public delegate string GeneralEventHandler();
- public class Publishser
- {
- public event GeneralEventHandler NumberChanged;
- public List<string> DOSomething()
- {
- List<string> strList = new List<string>();
- if (NumberChanged == null) return strList;
- Delegate[] generalEventHandlers = NumberChanged.GetInvocationList();
- foreach (Delegate generalEventHandler in generalEventHandlers)
- {
- // GeneralEventHandler mothed = (GeneralEventHandler)generalEventHandler;
- string rtn = generalEventHandler.DynamicInvoke(null).ToString();
- strList.Add(rtn);
- }
- return strList;
- }
- }
- public class OneScriber
- {
- public string OnNumberChanged()
- {
- return "One Subscriber";
- }
- }
- public class TwoScriber
- {
- public string OnNumberChanged()
- {
- return "Two Subscriber";
- }
- }
- public class ThreeScriber
- {
- public string OnNumberChanged()
- {
- return "Three Subscriber";
- }
- }
- }
結果如下:
還是一樣的結果.
委托的定義會生成繼承自MulticastDelegate的完整的類,其中包含Invoke()、BeginInvoke()和EndInvoke()方法。當我們直接調用委托時,實際上是調用了Invoke()方法,它會中斷調用它的客戶端,然後在客戶端線程上執行所有訂閱者的方法(客戶端無法繼續執行後面代碼),最後將控制權返回客戶端。注意到BeginInvoke()、EndInvoke()方法,在.Net中,異步執行的方法通常都會配對出現,並且以Begin和End作為方法的開頭(最常見的可能就是Stream類的BeginRead()和EndRead()方法了)。它們用於方法的異步執行,即是在調用BeginInvoke()之後,客戶端從線程池中抓取一個閒置線程,然後交由這個線程去執行訂閱者的方法,而客戶端線程則可以繼續執行下面的代碼。
BeginInvoke()接受“動態”的參數個數和類型,為什麼說“動態”的呢?因為它的參數是在編譯時根據委托的定義動態生成的,其中前面參數的個數和類型與委托定義中接受的參數個數和類型相同,最後兩個參數分別是AsyncCallback和Object類型,對於它們更具體的內容,可以參見下一節委托和方法的異步調用部分。現在,我們僅需要對這兩個參數傳入null就可以了。另外還需要注意幾點:
在委托類型上調用BeginInvoke()時,此委托對象只能包含一個目標方法,所以對於多個訂閱者注冊的情況,必須使用GetInvocationList()獲得所有委托對象,然後遍歷它們,分別在其上調用BeginInvoke()方法。如果直接在委托上調用BeginInvoke(),會拋出異常,提示“委托只能包含一個目標方法”。
如果訂閱者的方法拋出異常,.Net會捕捉到它,但是只有在調用EndInvoke()的時候,才會將異常重新拋出。而在本例中,我們不使用EndInvoke()(因為我們不關心訂閱者的執行情況),所以我們無需處理異常,因為即使拋出異常,也是在另一個線程上,不會影響到客戶端線程(客戶端甚至不知道訂閱者發生了異常,這有時是好事有時是壞事)
BeginInvoke()方法屬於委托定義所生成的類,它既不屬於MulticastDelegate也不屬於Delegate基類, 我們需要進行一個向下轉換,來獲取到實際的委托類型。
示例:
- namespace DelegateAndEvent
- {
- class Program
- {
- static void Main(string[] args)
- {
- Publishser pub = new Publishser();
- OneScriber oneSub = new OneScriber();
- TwoScriber twoSub = new TwoScriber();
- ThreeScriber threeSub = new ThreeScriber();
- pub.NumberChanged += new GeneralEventHandler(oneSub.OnNumberChanged);
- pub.NumberChanged += new GeneralEventHandler(twoSub.OnNumberChanged);
- pub.NumberChanged += new GeneralEventHandler(threeSub.OnNumberChanged);
- List<string> strlist = pub.DOSomething();
- foreach (string result in strlist)
- Console.WriteLine(result);
- System.Threading.Thread.Sleep(5000);
- }
- }
- public delegate string GeneralEventHandler(object sender,EventArgs e);
- public class Publishser
- {
- public event GeneralEventHandler NumberChanged;
- public List<string> DOSomething()
- {
- List<string> strList = new List<string>();
- if (NumberChanged == null) return strList;
- Delegate[] generalEventHandlers = NumberChanged.GetInvocationList();
- foreach (Delegate generalEventHandler in generalEventHandlers)
- {
- GeneralEventHandler mothed = (GeneralEventHandler)generalEventHandler;
- IAsyncResult result = mothed.BeginInvoke(this, EventArgs.Empty, null, null);
- string str = mothed.EndInvoke(result);
- strList.Add(str);
- }
- return strList;
- }
- }
- public class OneScriber
- {
- public string OnNumberChanged(object sender,EventArgs e)
- {
- return "One Subscriber";
- }
- }
- public class TwoScriber
- {
- public string OnNumberChanged(object sender, EventArgs e)
- {
- return "Two Subscriber";
- }
- }
- public class ThreeScriber
- {
- public string OnNumberChanged(object sender, EventArgs e)
- {
- return "Three Subscriber";
- }
- }
- }
結果:
BeginInvoke的另外兩個參數分別是AsyncCallback和Object類型,其中AsyncCallback是一個委托類型,它用於方法的回調,即是說當異步方法執行完畢時自動進行調用的方法。它的定義為:
public delegate void AsyncCallback(IAsyncResult ar);
Object類型用於傳遞任何你想要的數值,它可以通過IAsyncResult的AsyncState屬性獲得。
原文鏈接:http://www.cnblogs.com/springyangwc/archive/2011/06/20/2085541.Html
【編輯推薦】