Event事件和釋出者/訂閱者模式
很多程式都有這樣的一個需求,當一個特定的事件發生時,程式的其他部分能夠得到通知,並且需要做一些事情。這個時候就需要事件了。
釋出者/訂閱者模式
釋出者/訂閱者模式(publisher/subscriber pattern)就是滿足這種需求,設計模式中也叫觀察者模式。釋出者儲存一個方法集合,並且提供一個註冊方法,讓訂閱者把自己的方法註冊進去,這樣在事件發生的時候,釋出者可以呼叫註冊到儲存集合中的所有方法。
有以下要點:
- 釋出者(publisher)
- 訂閱者(subscriber)
- 事件處理程式(event handler)訂閱者註冊到釋出者的方法
- 觸發(raise)事件 當事件被呼叫(invoke)或觸發(fire)時,所有註冊的事件處理程式會被依次呼叫
使用步驟
- 宣告委託型別
- 宣告事件
- 事件處理程式宣告
- 訂閱事件/註冊事件
- 觸發事件(呼叫事件)
程式碼示例:
using EventDemo; void Main() { Publisher pub = new Publisher(); //釋出者物件 Subscriber sub = new Subscriber(); //訂閱者物件 pub.BtnPressEvent += sub.ProcBtnPress; //4、訂閱事件(或者說給事件新增處理程式) pub.ButtonPress(); //5、觸發事件 } namespace EventDemo { //1、宣告委託型別 delegate void DelButton(object sender, int id); //釋出者型別 class Publisher { //2、宣告一個事件成員變數 public event DelButton BtnPressEvent; //事件觸發方法 public void ButtonPress() { Button button1 = new Button(); if (BtnPressEvent != null) { BtnPressEvent(button1, 10); } } } //訂閱者型別 class Subscriber { //3、事件處理程式(按鈕按下處理) public void ProcBtnPress(object sender, int id) { Console.WriteLine($"Id of button is {id}"); } } }//namespace
1、宣告委託型別
//1、宣告委託型別
delegate void DelButton(object sender, int id);
事件的宣告需要依賴此委託型別,並且事件處理程式的格式要與此委託保持一致。事件與委託很像,它包含了一個私有的內部委託。
- 事件的委託是私有的,無法被直接訪問
- 事件提供的操作比委託少,一般只有新增、刪除和呼叫事件處理程式
- 事件觸發時,它通過呼叫內部的委託,來依次呼叫存放在委託方法列表中的事件處理程式
2、宣告事件變數
語法格式:
public event 委託型別 事件名稱;
//2、宣告一個事件成員變數,並且被隱式自動初始化為null public event DelButton BtnPressEvent; public static event DelButton BtnPressEvent; //宣告為靜態成員,區域性於類 public event DelButton Event1, Event2, Event3; //同時宣告多個事件
注意,事件是一個成員變數,而不是型別,它可以宣告在類和結構中。這裡使用public修飾後,外部的程式才可以向該事件註冊處理程式。
BCL中有一個EventHandler的委託,專門用於系統事件。
3、事件處理程式
在訂閱者型別中,定義了事件處理程式,該方法的格式必須保持和委託型別一致,要有相同的返回值和方法簽名。該事件處理程式用於在事件觸發時,被事件呼叫。最典型的就是響應winform或wpf中控制元件的事件,譬如像按鈕按下、下拉框選擇更改、文字框內容修改的事件等等。
//訂閱者型別
class Subscriber
{
//3、事件處理程式(按鈕按下處理)
public void ProcBtnPress(object sender, int id)
{
Console.WriteLine($"Id of button is {id}");
}
}
4、訂閱事件
有了事件處理程式後,就可以把我們新增的方法註冊到事件上了,或者說訂閱該事件。在示例中,我們把自定義的事件處理方法ProcBtnPress通過+=的方式,新增到事件中去,這和委託增加方法的方式一致。
這樣一來,可以把多個訂閱者物件的自定義方法新增到事件中,又叫做多個訂閱者訂閱了同一個釋出者的事件。
void Main()
{
Publisher pub = new Publisher(); //釋出者物件
Subscriber sub = new Subscriber(); //訂閱者物件
pub.BtnPressEvent += sub.ProcBtnPress; //4、訂閱事件(或者說給事件新增處理程式)
pub.ButtonPress(); //5、觸發事件
}
5、事件觸發
在示例中,我們在釋出者型別中,寫了一個事件觸發的方法。事件觸發其實就是在某個需要的地方,呼叫該事件,內部會依次執行委託中的方法。
//釋出者型別
class Publisher
{
//2、宣告一個事件成員變數
public event DelButton BtnPressEvent;
//3、事件觸發方法
public void ButtonPress()
{
Button button1 = new Button();
if (BtnPressEvent != null)
{
BtnPressEvent(button1, 10);
}
}
}
然後在外部呼叫事件觸發方法。
void Main()
{
Publisher pub = new Publisher(); //釋出者物件
Subscriber sub = new Subscriber(); //訂閱者物件
pub.BtnPressEvent += sub.ProcBtnPress; //4、訂閱事件(或者說給事件新增處理程式)
pub.ButtonPress(); //5、觸發事件
}
標準事件的使用
上面說到BCL中有一個EventHandler的委託,專門用於系統事件,它是.Net框架提供的標準模式。
//sender: 發起事件的物件的引用,型別是object可以相容所有型別,但是使用的時候需要把object轉成對應型別
//s: 觸發事件傳進來的引數基類引用
public delegate void EvnetHandler(object sender, EventArgs e);
第二個引數要注意, 型別EventArgs是引數基類型別, 並不能直接使用, 要傳遞引數必須要派生一個引數類繼承自EventArgs用來傳遞引數。
EventArgs類的定義:
// System.EventArgs
using System;
using System.Runtime.CompilerServices;
[Serializable]
[TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
public class EventArgs
{
public static readonly EventArgs Empty = new EventArgs();
}
下面我們使用標準事件委託EventHandler和自定義引數類,來實現事件的使用
using EventDemo;
void Main()
{
CustomButton btn = new CustomButton(); //釋出者物件
Subscriber sub = new Subscriber(); //訂閱者物件
btn.BtnPressEvent += sub.ProcBtnPress; //訂閱事件
btn.Click(); //事件觸發
}
namespace EventDemo
{
//釋出者類, 這裡寫了一個簡單的自定義按鈕類作為釋出者
class CustomButton
{
//2、這裡我們使用系統的標準事件委託來宣告事件變數
//這裡的格式是使用了泛型委託,將預設的EventArgs引數類替換成我們自定義的
public event EventHandler<CustomButtonEventArgs> BtnPressEvent;
//自定義引數變數
private CustomButtonEventArgs buttonEventArgs = new CustomButtonEventArgs();
//3、事件觸發方法
public void Click()
{
buttonEventArgs.Id = 10;
buttonEventArgs.Value = 255;
if (BtnPressEvent != null)
{
BtnPressEvent(this, buttonEventArgs);
}
}
}
//訂閱者類
class Subscriber
{
//4、自定義事件處理程式(按鈕按下處理)
//注意,這裡的引數要和標準委託EventHandler保持一致
public void ProcBtnPress(object sender, EventArgs e)
{
Console.WriteLine($"The publisher Type is {sender.GetType().Name}");
CustomButtonEventArgs ea = e as CustomButtonEventArgs;
Console.WriteLine($"The eventArgs is {ea.Id} and {ea.Value}");
}
}
//1、自定義按鈕的引數類
class CustomButtonEventArgs : EventArgs
{
//這裡的引數可以存放一些外部的自定義資料
public int Id { get; set; }
public int Value { get; set; }
}
}//namespace