1. 委托
事件是利用委托來定義的,因此先解釋委托。委托其實是一個類,它與其他類如string(引用類型)等沒有本質區別,string代表的是字符串,委托則代表的是一類方法,這類方法具有相同返回類型和相同參數。例:有如下委托定義
public delegate void CalculatorHandler(int x,int y);
從CalculatorHandler這個委托的定義可以看出,它反應的是一類方法,這類方法的返回類型是void,兩個參數是(int x,int y),因此以后所有具有這樣特征的方法都可以用這個委托來代替,現有以下這個方法:
static void Add(int x, int y) { Console.WriteLine("x+y={0}",x+y); }
這個方法,滿足委托所定義的這一類方法,但是如何利用委托來調用這個方法呢,首先要將這個方法賦值給這個委托才可以使用,就如普通的類賦值一樣,
CalculatorHandler calhandler = new CalculatorHandler(Add)
當然你也可以像給一個字符串賦值一樣,這樣給委托賦值:
CalculatorHandler calhander = Add;
當要調用這個方法的時候,你可以就像使用方法一樣使用委托,
calhander(3,4);
委托也可以綁定多個方法,當調用這個委托時,會調用所有已經綁定了的方法,如現在還有這樣的方法:
static void Multiply(int x, int y) { Console.WriteLine("x*y={0}",x*y); }
只需在calhandler基礎上多綁定個方法,具體語法:
calhander += Multiply;
這樣當調用
calhander(3,4);
會同時調用Add和Multiply這兩個方法,你也可以利用“-=”解綁方法:
calhander -= Multiply;
這樣再次調用這個委托,則只會調用Add這個方法。
2. 事件
一說到事件總會有發布者(publisher)和訂閱者(subscriber),發布者定義了一個事件,訂閱者訂閱了該事件(指的是當該事件觸發時,訂閱者做出什么樣的反應,即利用相應的函數去處理)。該函數的定義與定義該事件的委托必須配套。代碼如下:
public delegate void MessageEventHandler(); class Publisher { public event MessageEventHandler MessageEvent; public void DoSomething() { Console.WriteLine("等待消息"); Console.WriteLine("首長來啦!!!"); OnMessageEvent(); } public void OnMessageEvent() { if (MessageEvent != null) { MessageEvent(); } } } class Subscriber { public Subscriber(Publisher p) { p.MessageEvent += Response; } public void Response() { Console.WriteLine("首場,辛苦了"); } } class Program { static void Main(string[] args) { Publisher p = new Publisher(); Subscriber s = new Subscriber(p); p.DoSomething(); Console.ReadKey(); } } }
這就是事件的基本用法,但是事件與委托到底有什么區別呢,從上面的代碼可以看出,事件是根據委托來定義的,
public event MessageEventHandler MessageEvent
其實它是利用委托來規定訂閱者處理函數的類型(相同的返回類型和參數即為一類),然后方便在發布者自身的類中來觸發訂閱者的這些函數。
但是為什么要事件呢,要實現這些,我僅用委托也可以實現呀,如下代碼:
public delegate void MessageEventHandler(); class Publisher { public MessageEventHandler MessageEvent;//為了方便,委托名與原來的事件名相同。 // public event MessageEventHandler MessageEvent; public void DoSomething() { Console.WriteLine("等待消息"); Console.WriteLine("首長來啦!!!"); OnMessageEvent(); } public void OnMessageEvent() { if (MessageEvent != null) { MessageEvent(); } } } class Subscriber { public Subscriber(Publisher p) { p.MessageEvent += Response; } public void Response() { Console.WriteLine("首場,辛苦了"); } } class Program { static void Main(string[] args) { Publisher p = new Publisher(); Subscriber s = new Subscriber(p); p.DoSomething(); Console.ReadKey(); } }
這樣子也同樣可以產生相同的結果。
但是當客戶端如下調用呢?
Publisher p = new Publisher(); Subscriber s = new Subscriber(p); p.DoSomething(); p.MessageEvent(); Console.ReadKey();
客戶端是不是能隨意讓發布者產生事件,因此我們可否將發布者的委托定義為private,因為只有發布者的內部才能觸發事件嘛,其他人怎么可以?但是這樣訂閱者就無法訂閱了,那我們是否可以增加綁定和解除的函數來訂閱此委托呢?
public delegate void MessageEventHandler(); class Publisher { private MessageEventHandler MessageEvent; // public event MessageEventHandler MessageEvent; public void DoSomething() { Console.WriteLine("等待消息"); Console.WriteLine("首長來啦!!!"); OnMessageEvent(); } public void OnMessageEvent() { if (MessageEvent != null) { MessageEvent(); } } public void Add_MessageEvent(MessageEventHandler m) { MessageEvent += m; } public void Reomove_MessageEvent(MessageEventHandler m) { MessageEvent -= m; } } class Subscriber { public Subscriber(Publisher p) { //p.MessageEvent += Response; p.Add_MessageEvent(Response); } public void Response() { Console.WriteLine("首場,辛苦了"); } } class Program { static void Main(string[] args) { Publisher p = new Publisher(); Subscriber s = new Subscriber(p); p.DoSomething(); Console.ReadKey(); } } }
這樣就可以實現與事件一樣的功能,因此為了方便微軟為我們提供了事件訪問器,何為事件訪問器?
public delegate void MessageEventHandler(); class Publisher { private MessageEventHandler MessageEvent; // public event MessageEventHandler MessageEvent; public event MessageEventHandler TestEvent { add { lock (MessageEvent) { MessageEvent += value; } } remove { lock (MessageEvent) { MessageEvent -= value; } } } public void DoSomething() { Console.WriteLine("等待消息"); Console.WriteLine("首長來啦!!!"); OnMessageEvent(); } public void OnMessageEvent() { if (MessageEvent != null) { MessageEvent(); } } } class Subscriber { public Subscriber(Publisher p) { p.TestEvent += Response; } public void Response() { Console.WriteLine("首場,辛苦了"); } } class Program { static void Main(string[] args) { Publisher p = new Publisher(); Subscriber s = new Subscriber(p); p.DoSomething(); Console.ReadKey(); } }
以上的代碼就是利用事件訪問器來讓委托綁定訂閱者的方法,事件訪問器中的add和remove能自動編譯為+=和-=。
通過reflector,我們可以看到一個事件是如何定義的:
它是申明一個私有的委托:
private MessageEventHandler MessageEvent;
另外利用兩個函數來綁定與解除訂閱者的方法。