程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 再談談C#中的委托和事件

再談談C#中的委托和事件

編輯:C#入門知識

引言

如果你閱讀過 aspx.com/dotnet/csharp/csharp_20091008_768.html" target=_self>C#中的委托和事件 一文,我想你對委托和事件已經有了一個基本的認識。但那些遠不是委托和事件的全部內容,還有很多的地方沒有涉及。本文將討論委托和事件一些更為細節的問題,包括一些大家常問到的問題,以及事件訪問器、異常處理、超時處理和異步方法調用等內容。

為什麼要使用事件而不是委托變量?

在 C#中的委托和事件 中,提出了兩個為什麼在類型中使用事件向外部提供方法注冊,而不是直接使用委托變量的原因。主要是從封裝性和易用性上去考慮,但是還漏掉了一點,事件應該由事件發布者觸發,而不應該由客戶端(客戶程序)來觸發。這句話是什麼意思呢?請看下面的范例:

NOTE:注意這裡術語的變化,當我們單獨談論事件,我們說發布者(publisher)、訂閱者(subscriber)、客戶端(client)。當我們討論Observer模式,我們說主題(subject)和觀察者(observer)。客戶端通常是包含Main()方法的Program類。

class Program {
    static void Main(string[] args) {
        Publishser pub = new Publishser();
        Subscriber sub = new Subscriber();
       
        pub.NumberChanged += new NumberChangedEventHandler(sub.OnNumberChanged);
        pub.DoSomething();          // 應該通過DoSomething()來觸發事件
        pub.NumberChanged(100);     // 但可以被這樣直接調用,對委托變量的不恰當使用
    }
}

// 定義委托
public delegate void NumberChangedEventHandler(int count);

// 定義事件發布者
public class Publishser {
    private int count;
    public NumberChangedEventHandler NumberChanged;         // 聲明委托變量
    //public event NumberChangedEventHandler NumberChanged; // 聲明一個事件

    public void DoSomething() {
        // 在這裡完成一些工作 ...

        if (NumberChanged != null) {    // 觸發事件
            count++;
            NumberChanged(count);
        }
    }
}

// 定義事件訂閱者
public class Subscriber {
    public void OnNumberChanged(int count) {
        Console.WriteLine("Subscriber notified: count = {0}", count);
    }
}

上面代碼定義了一個NumberChangedEventHandler委托,然後我們創建了事件的發布者Publisher和訂閱者Subscriber。當使用委托變量時,客戶端可以直接通過委托變量觸發事件,也就是直接調用pub.NumberChanged(100),這將會影響到所有注冊了該委托的訂閱者。而事件的本意應該為在事件發布者在其本身的某個行為中觸發,比如說在方法DoSomething()中滿足某個條件後觸發。通過添加event關鍵字來發布事件,事件發布者的封裝性會更好,事件僅僅是供其他類型訂閱,而客戶端不能直接觸發事件(語句pub.NumberChanged(100)無法通過編譯),事件只能在事件發布者Publisher類的內部觸發(比如在方法pub.DoSomething()中),換言之,就是NumberChanged(100)語句只能在Publisher內部被調用。

大家可以嘗試一下,將委托變量的聲明那行代碼注釋掉,然後取消下面事件聲明的注釋。此時程序是無法編譯的,當你使用了event關鍵字之後,直接在客戶端觸發事件這種行為,也就是直接調用pub.NumberChanged(100),是被禁止的。事件只能通過調用DoSomething()來觸發。這樣才是事件的本意,事件發布者的封裝才會更好。

就好像如果我們要定義一個數字類型,我們會使用int而不是使用object一樣,給予對象過多的能力並不見得是一件好事,應該是越合適越好。盡管直接使用委托變量通常不會有什麼問題,但它給了客戶端不應具有的能力,而使用事件,可以限制這一能力,更精確地對類型進行封裝。

NOTE:這裡還有一個約定俗稱的規定,就是訂閱事件的方法的命名,通常為“On事件名”,比如這裡的OnNumberChanged。

為什麼委托定義的返回值通常都為void?

盡管並非必需,但是我們發現很多的委托定義返回值都為void,為什麼呢?這是因為委托變量可以供多個訂閱者注冊,如果定義了返回值,那麼多個訂閱者的方法都會向發布者返回數值,結果就是後面一個返回的方法值將前面的返回值覆蓋掉了,因此,實際上只能獲得最後一個方法調用的返回值。可以運行下面的代碼測試一下。除此以外,發布者和訂閱者是松耦合的,發布者根本不關心誰訂閱了它的事件、為什麼要訂閱,更別說訂閱者的返回值了,所以返回訂閱者的方法返回值大多數情況下根本沒有必要。

class Program {
    static void Main(string[] args) {
        Publishser pub = new Publishser();
        Subscriber1 sub1 = new Subscriber1();
        Subscriber2 sub2 = new Subscriber2();
        Subscriber3 sub3 = new Subscriber3();

        pub.NumberChanged += new GeneralEventHandler(sub1.OnNumberChanged);
        pub.NumberChanged += new GeneralEventHandler(sub2.OnNumberChanged);
        pub.NumberChanged += new GeneralEventHandler(sub3.OnNumberChanged);
        pub.DoSomething();         

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved