天天看點

C#學習筆記:使用事件

事件與委托有着密切的關系,因為事件自身就是委托類型。由于委托可以綁定和調用多個方法,是以會為時間的處理帶來友善。類型隻需要對外公開事件,就可以與外部的其他地方進行關聯,進而實作事件訂閱。

首先,我們先介紹一下事件訂閱的概念。在實際生活中,假設我們訂閱了某新聞平台的郵件通知服務,隻要有新聞更新,服務提供方就要發送通知郵件。是以,事件訂閱也是如此,前文分析過選用委托作為事件的類型的理由,就是委托可以與其他方法關聯,當A方法與X事件進行了關聯,隻要X事件發生(相當于新聞内容有更新),就會調用作為事件的委托(相當于服務提供方發送通知郵件),因為A方法與X事件關聯,是以A方法也會被調用,于是代碼就能夠響應事件。

響應事件就像戰士聽到了沖鋒号響了,知道該沖鋒了。戰士的反應就相當于處理事件的方法,而沖鋒号響起就相當于表示事件的委托被調用。

事件是委托類型,是以,需要在類中聲明事件,首先要定義用來作為事件封裝類型的委托,然後在類中用event關鍵字來聲明事件。通常會在類中聲明一個受保護的方法,習慣上命名為On<事件名>,然後在這個方法中調用事件。

  1. 首先建立一個項目,項目生成的命名空間中定義一個委托類型,作為響應按下空格鍵這一事件的封裝類型。
    public delegate void SpaceKeyPressEventHandler();
               
  2. 定義一個MyApp類,代碼如下:

public class MyApp

{

///

/// 聲明事件

///

public event SpaceKeyPressEventHandler SpaceKeyPressed;

//聲明了一個系統自帶的事件,即當按下空格鍵的事件

/// <summary>
    /// 通過該方法引發事件
    /// </summary>
    protected virtual void OnSpaceKeyPressed()
    {
        //觸發SpaceKeyPressed事件時必須判斷這個事件是不是與其他方法關聯了
        //如果這個委托類型的事件沒有關聯其他的方法,就不觸發事件
        if (this.SpaceKeyPressed != null)
        {
            SpaceKeyPressed();
        }
    }

    public void StartRun()
    {
        while (true)
        {
            ConsoleKeyInfo keyinfo = Console.ReadKey();
            if (keyinfo.Key == ConsoleKey.Spacebar)
            {
                //引發事件
                OnSpaceKeyPressed();
            }
            if (keyinfo.Key == ConsoleKey.Escape)
            {
                //跳出循環
                break;
            }

        }
        
    }

}
           

在這個類中,用剛才定義的委托聲明了一個SpaceKeyPressed事件。然後封裝自愛OnSpaceKeyPressed方法中調用,因為這個表是事件的委托可能是空的,調用方法可能不響應事件處理,即沒有與之關聯的方法。是以,我們在調用前必須先判斷一下表示事件的委托執行個體是否為null.

還有一種更簡單的寫法,不需要if語句進行盤多,而是直接調用事件,但在事件名稱後面加上一個“?”(英文的問号),代碼如下

protected virtual void OnSpaceKeyPressed()
    {
          this.SpaceKeyPressed?.Invoke();
    }
           

這樣寫之後,程式就會自動判斷SpaceKeyPressed是否為null,如果為null,這行代碼就不會被執行,直接跳過。如果不為null就執行這行代碼。

在StartRun方法中啟動了一個無限循環,也就先不要用下面這段代碼來跳出循環。

if (keyinfo.Key == ConsoleKey.Escape)
            {
                //跳出循環
                break;
            }
           

這樣,當按下的是Esc時,用break語句直接退出循環。

如果按下的鍵是空格鍵,就調用OnSpaceKeyPressed方法,這樣一來,事件就被觸發了。而代碼在使用MyApp類時,先建立一個執行個體,然後通過+=運算符使SpaceKeyPressed事件與相關的方法關聯起來。在VS中,隻要在事件名後面輸入+=,再按一下Tab鍵,這時候會生成一個預設的方法名(變量名_方法名),因為這個方法名處于標明狀态,我們可以對其進行重命名(如圖所示),如果不需要重命名,就再按一下Tab鍵,就會生成事件處理方法。

C#學習筆記:使用事件

我們将方法中的處理代碼修改如下:

C#學習筆記:使用事件

運作程式後,隻要按下空格鍵,螢幕上就會輸出提示資訊。

在引發事件時,很多時候都會考慮一下傳遞一些資料。比如一個捕捉滑鼠操作的事件,光是引發事件是不夠的,事件的處理程式還需要知道使用者進行了哪些滑鼠操作,是否移動了滑鼠指針,或者是按下了滑鼠上的某個鍵;如果是按下了滑鼠上的鍵,是左鍵還是右鍵…可見,在引發事件的時候,還有必要傳遞一些描述性的資訊,以便事件處理代碼能夠獲得更詳細的資料。

通常,作為事件委托,有兩個參數,一個是Object類型,表示引發事件的對象,即是誰引發了事件,多數情況下,在調用事件時是把類的目前執行個體引用(this)傳遞過去。另一個參數是從System.EventArgs派生的類的執行個體。這是一個标準的事件處理程式的簽名,為了規範事件的處理,.Net類庫已經定義好了一個System.EventHandler委托,用于聲明事件。它的原型如下:

public delegate void EventHandler(Object sender, System.EventArgs e);
           

引發事件的對象執行個體将傳遞給sender參數,參與事件的相關資料則傳遞給e參數,如果不需要傳遞過多的資料,可以通過System。EventArgs.Empty靜态成員傳回一個EventArgs對象來傳遞。

但是,由于不同的事件要傳遞的參數不同,更多時候是從EventArgs類派生的子類的執行個體,顯然一個EventHandler委托是不能滿足各種情況的。如果針對不同類型的事件也定義一個對應的委托,數量一旦多起來,既混亂又不好管理。為了解決這個問題,.NET類庫又提供了一個帶有泛型參數的事件處理委托。原型如下:

public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
           

TEventArgs是一個泛型參數,TEventArgs應該是System.EventArgs類或者System.EventArgs類的派生類型。

有了EventHandler委托,開發者就可以應對各種各樣的事件了。因為對于不同的事件,第一個參數是不變的,隻是第二個參數的類型有差異而已。