很多程序都有這樣的一個需求,當一個特定的事件發生時,程序的其他部分能夠得到通知,並且需要做一些事情。這個時候就需要事件了。
發布者/訂閱者模式
發布者/訂閱者模式(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
{
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
