標籤:style http color 使用 os 資料
事件與委託似乎很難以理解,這是因為它們的使用方式與常用的編碼有很大的差別,例如通常編寫的都是同步代碼,調用一個類型的方法,會即刻出現方法執行的結果,這是符合邏輯的。但在某些情況中,同步代碼未必滿足需求,拿公用汽車來打個比方,如果交通管制中心希望每一輛公車到達一個網站時都發送給自己一個訊號以便自己能夠隨時掌握交通狀況,使用同步代碼,公汽對象肯定需要調用管制中心對象,這樣就出現了我們一直不願意看到的情況:兩個類型緊密地耦合在一起。既然要其它類型對自己的行為作出反應,親自調用其類型的方法似乎不可避免,在同步代碼中,很難避免這種緊密的類型調用關係。
另一個差別是在一般情況下,我們只將屬性作為參數傳遞給方法,而很少會考慮將一個方法傳遞給另一個方法。
我們拋棄各種C#參考書中桀驁難懂的事件與委託概念,設想一個情景來理解事件與委託的使用:有一家IT公司,董事長不希望自己的僱員在上班時間玩遊戲,但又不可能每時每刻都盯著每個僱員,因此,他希望使用一種新的方式實現監視僱員的效果:如果有僱員違反規定,某個裝置或專門的監查人員將自動發出一個訊息通知他,董事長只需要在事情發生時進行處理。
因此,這個用例實際上是兩種類型——董事長類與僱員類——之間的互動,下面的代碼將給讀者展示如何使用委託與事件機制實現這種互動:
首先,我們需要在董事長類與僱員類之間定義一個委託類型,用於傳遞兩者之間的事件,這個類型就是一個監視裝置或專門負責打小報告的監查人員:
public delegate void DelegateClassHandle();
定義一個委託的過程類似方法的定義,但它沒有方法體。定義委託一定要添加關鍵字delegate。由於定義委託實際上相當一個類,因此可以在定義類的任何地方定義委託。另外,根據委託的可見度,也可以添加一般的存取修飾詞,如public、private和protected。
委託的傳回值類型為void,這並非表示委託類型本身帶有傳回值,該傳回值類型是指委託的目標函數類型,即它委託的一個事件處理函數傳回值是void類型。
建立一個僱員類Employee,其代碼如下:
public class Employee
{
public event DelegateClassHandle PlayGame;
public void Games()
{
if (PlayGame != null)
{
PlayGame();
}
}
}
僱員類Employee代碼中定義了一個DelegateClassHandle類型的事件PlayGame,它的定義方式也很特殊,首先必須使用關鍵字event,表示PlayGame是一個事件,同時還必須聲明該事件的委託類型為DelegateClassHandle,即將來由該類型的委派物件負責通知事件。
如果有僱員開始玩遊戲,它將執行Games方法,而只要該方法一被調用,就會觸發一個事件PlayGame,然後董事長就會收到這個事件的訊息——有人在玩遊戲了。
董事長類代碼如下,他有一個方法Notify用於接收訊息:
public class Admin
{
public void Notify()
{
System.Console.WriteLine("someone is playing game");
}
}
Employee的PlayGame事件如何與Admin的Notify方法關聯起來呢?只需通過事件綁定即可實現,具體過程如下列代碼:
Employee employee = new Employee();
Admin admin = new Admin();
employee.PlayGame += new DelegateClassHandle(admin.Notify);
employee.Games();
請大家注意事件綁定的代碼:
employee.PlayGame += new DelegateClassHandle(admin.Notify);
通過DelegateClassHandle將兩個類的互動進行了綁定,當employee.Games方法調用後,觸發PlayGame事件,而該事件將被委託給admin的Notify方法處理,通知董事長有僱員在上班時間玩遊戲。
但董事長並不滿足這種簡單的通知,他還想知道究竟是誰在上班時間違反規定。顯然,現在委派物件必須傳遞必要的參數才行,這個要求也可以很容易地辦到。事件的參數可以設定為任何類型的資料,在.NET架構中,還提供了事件參數基類EventArgs專門用於傳遞事件數目據。
從該EventArgs類派生一個自訂的事件參數類CustomeEventArgs,這個類型將攜帶僱員姓名和年齡資訊:
public class CustomeEvetnArgs : EventArgs
{
string name = "";
int age = 0;
public CustomeEvetnArgs()
{ }
public string Name
{
get { return this.name; }
set { this.name = value; }
}
public int Age
{
get { return this.age; }
set { this.age = value; }
}
}
修改委託類型DelegateClassHandle的定義,讓其攜帶必要的參數:
public delegate void DelegateClassHandle(object sender, CustomeEvetnArgs e);
僱員類的代碼修改後如下:
public class Employee
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
private int _age;
public int Age
{
get { return _age; }
set { _age = value; }
}
public event DelegateClassHandle PlayGame;
public void Games()
{
if (PlayGame != null)
{
CustomeEvetnArgs e = new CustomeEvetnArgs();
e.Name = this._name ;
e.Age = this._age;
PlayGame(this, e);
}
}
}
在Games方法中,首先建立一個CustomeEventArgs對象,然後設定了必要的屬性Name和Age。
董事長的通知方法也必須相應地進行修改:
public class Admin
{
public void Notify(object sender, CustomeEvetnArgs e)
{ 軟體開發網
System.Console.WriteLine(e.Name+" is "+e.Age.ToString());
}
}
將兩個類型對象進行關聯的代碼也需要進行相應的修改:
Employee employee = new Employee();
employee.Name = "Mike";
employee.Age = 25;
Admin admin = new Admin();
employee.PlayGame += new DelegateClassHandle(admin.Notify);
employee.Games();
修改後的代碼啟動並執行結果是,當Mike調用Games方法玩遊戲時,會自動觸發PlayGame事件,而該事件攜帶相關語音總機admin,後者的Notify方法將接收到資料並輸出“Mike is 25”,告訴董事長是Mike,25歲,正在上班時間玩遊戲。
委託是可以多路廣播(Mulitcast)的,即一個事件可以委託給多個對象接收並處理。在上面的用例中,如果有另一位經理與董事長具有同樣的癖好,也可以讓委派物件將僱員的PlayGame事件通知他。
首先定義經理類:
public class Manager
{
public void Notify(object sender, CustomeEvetnArgs e)
{
System.Console.WriteLine(sender.ToString() + "-" + e.Name);
}
}
經理Manager類型的Notify方法與Admin一致,他也接受到相應的資訊。
委託的多路廣播綁定的方法仍然是使用+=運算子,其方法如下面的代碼所示:
Employee employee = new Employee();
employee.Name = "Mike";
employee.Age = 25;
Admin admin = new Admin();
Manager manager = new Manager();
employee.PlayGame += new DelegateClassHandle(admin.Notify);
employee.PlayGame += new DelegateClassHandle(manager.Notify);
employee.Games();
執行該方法,讀者將看到admin和manager的Notify方法都會被事件通知並調用執行。通過這樣的方法,董事長和經理都會知道Mike在玩遊戲了。
如果董事長不希望經理也收到這個通知,該如何解除PlayGame對manager的事件綁定呢?同樣非常簡單,在employee.Games方法被調用前執行下列語句即可:
employee.PlayGame -= new DelegateClassHandle(manager.Notify);
最後需要提醒讀者注意的,Employee類中的Games方法在觸發事件PlayGame之前需要判斷該事件是否為null。當 employee對象的Games方法觸發事件PlayGame後,必須有一個目標函數來處理這個事件,而該語句正是判斷該目標函數是否存在。如果將這個判斷去掉,且對事件不進行任何綁定而直接調用Games方法,程式將在事件PlayGame處彈出一個NullReferenceException的異常。
(本文引用自http://www.mscto.com/Csharp/20080626280.html)