0. 前言
事件和委托是C#中的高級特性,也是C#中很有意思的一部分,出現事件的地方,必然有委托出現;而委托則不一定會有事件出現,那為什么會出現這樣的關系呢?這就需要從事件和委托的定義出發,了解其中的內在,
1. 委托
說起委托,就不得不回憶一下之前在Linq篇中介紹的匿名方法,其中提到了Func和Action這兩個型別,這兩個型別就是委托,
委托在C#中定義為一種面向物件形式的方法尋址方案,簡單來講,就是定義一個型別,然后表示這個型別代表某一種方法,而委托物件,就是方法引數化,委托可以實作將方法當做一個引數傳遞給另一個方法,也可以認為是反射中的MethodInfo的一種特例(實際上并沒有太多關系),
委托不關心方法叫什么,也不關心方法從哪來(歸屬于哪個類或者哪個物件),只關心方法需要哪些引數,回傳什么型別,
說到這里,我們來看一下如何定義一個委托吧,委托的定義形式如下:
delegate <回傳型別> 委托名(引數串列);//引數串列代表任意個引數
由之前的定義形式,我們可以知道委托也是一種型別,所以它的定義也符合型別的定義規范,現在我們定義一個沒有回傳值也沒有引數型別的委托作為我們創建的第一個委托:
public delegate void FirstDel();// 型別名稱是 FirstDel
簡單的使用一下:
FirstDel del ;
del();// 會直接報錯
上述代碼如果運行的話,會很直接的報錯,因為你沒有告訴編譯器變數del 應該是什么,也就是沒有為del賦值,同時委托可以賦值為null,所以在使用的時候需要注意不能為null,否者也是無法運行的,
這里應用匿名方法的話,可以按照下面的代碼對del進行賦值:
del = ()=>
{
//省略方法
}
那么我們熱身結束,開始正式創建一個有意義的委托:
public delegate decimal CalculateArea(decimal height, decimal weight);
上述委托宣告了一個計算面積的規范,使用長寬進行面積計算,那么我們來為它賦值:
CalculateArea squrare = (height, weight) => height * height;// 正方形
CalculateArea rectangle = (height, weight) => height * weight;// 矩形
CalculateArea triangle = (height, weight) => height * weight / 2; //三角形
我們依次創建了三個計算面積的方法,分別是正方形、矩形、三角形,分別呼叫它們將會得到對應的計算結果:
var squrareArea = squrare(10, 10);// 100
var rectangleArea = rectangle(19, 10);//190
var triangleArea = triangle(10, 5);//25
特別的,C#中委托支持多路廣播,所以也可以使用+、-進行注冊和洗掉,多路廣播是指在事件和委托中有多個監聽器或回應方法,當事件觸發或者委托呼叫的時候,注冊的方法組將會都呼叫,當使用這種方式對委托進行賦值的時候,委托將自動轉為方法組,簡單理解就是 委托物件內部創建了一個串列,然后把賦值給它的方法都存進去了,
所以就會產生如下操作:
CalculateArea calculate = squrare;// calculate必須先賦值一個方法
calculate += rectangle;// 增加 矩形的面積計算方法
calculate += triangle; // 增加三角形的面積計算方法
calculate -= triangle; // 減去三角形的面積計算方法
到這里會產生一個疑問,calculate運行結果是什么,會回傳一個陣列或者其他型別嗎?顯然不會,因為calculate定義的回傳型別就是一個decimal,所以不會回傳其他的值,
嗯,這就產生了另一個疑問,回傳的是哪一個方法的計算結果呢,其他方法的計算結果呢?這里告訴大家一個結果,只會回傳最后一次注冊的方法的執行結果,其他的方法執行了,但是方法的執行結果無法用變數接到,
所以這里有一個很重要的實踐,如果有需要把委托當做一個方法串列進行使用的時候,最好宣告為void或者拋棄回傳值的具體內容,
2. 事件
事件,event,在C#中,事件就像是一種機制,在程式運行到一定階段的時候或者遇到某些狀況的時候,就會觸發一個事件,然后如果有其他代碼訂閱了這個事件,就會自動執行訂閱的代碼,描述起來很抽象,簡單來講就是在類宣告一個委托,并標記這個委托是一個事件,在另一個方法中執行這個事件,其中,觸發這個事件的類稱為發布者,接受或者注冊了處理方法的類稱為訂閱者,
如何創建或宣告一個事件?宣告一個事件有兩種方式,一種是直接使用EventHandler ,另一種是自己先定義一個委托,然后用這個委托定義事件,
1. 使用EventHandler
public class EventDemo
{
public event EventHandler HandlerEvent;
}
2. 使用自定義委托
public class EventDemo
{
public delegate void EventDelegate(object sender, EventArgs e);
public event EventDelegate DelegateEvent;
}
一般事件的定義約定俗稱是一個void方法,第一個引數是sender表示事件的發布者,默認是object型別,第二個引數是EventArgs型別的事件變數,表示觸發事件時需要訂閱者注意的內容,一般用來傳一些引數,
其中 EventHandler有一個泛型版本,其宣告如下:
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
其第二個引數并沒有對TEventArgs進行限制,所以我們可以用任何型別當做事件變數,
我們再來看看,EventArgs里有什么,什么都沒有,只有一個默認構造方法和幾個繼承自Object的方法,所以在開發中,我們會自己定義一個事件變數型別,為了保持一致會繼承EventArgs,
C#建議事件的定義以On開頭,表示在什么時觸發,示例代碼并不符合這個規范,
3. 使用一下事件和委托
創建一個帶事件的類:
public class EventDemo
{
public delegate void EventDelegate(object sender, EventArgs e);
public event EventDelegate DelegateEvent;
public void Trigger()
{
if (DelegateEvent != null)// 觸發事件,按需判斷事件的訂閱者串列是否為空
{
DelegateEvent(this, new EventArgs());
}
}
}
使用一下:
EventDemo demo = new EventDemo();
demo.DelegateEvent += (sender, eventArgs) =>
{
//省略訂閱者的方法內容
}
demo.Trigger();//觸發事件
當發布者嘗試觸發事件的時候,訂閱者將會接收到訊息,然后注冊訂閱者方法就會被呼叫,發布者向訂閱者傳遞一對sender和eventArgs,訂閱者按照自己的邏輯進行處理,
這里很明顯可以看出,事件的處理程式注冊方法用的+=,所以與之對應的也有一個-=表示取消訂閱,
到這里,委托和事件的基本概念就已經介紹完畢了,當然還是那句話,更多的內容在實踐中,C#的事件機制讓程式員有更多的自由去自定義事件,而不是被局限在某些框架內,所以大家可以多試試C#的事件,也許能發現更多的我不知道的內容呢,
更多內容煩請關注我的博客

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/49160.html
標籤:C#
