昨天,通過《C# 知識回顧 - 事件入門》介紹了事件的定義及簡單用法,今天我們通過控制台來看下“發布 - 訂閱”的基本用法。
下面的過程演示了如何將符合標准 .NET 模式的事件添加到您的類和結構中。.NET類庫中的所有事件均基於 EventHandler 委托,定義如下:
public delegate void EventHandler(object sender, EventArgs e);
你可以嘗試手動輸入 EventHandler ,然後按下“F12”跳轉到定義:
.NET Framework 2.0 引入了此委托的一個泛型版本,即 EventHandler<TEventArgs>。
【備注】雖然您定義的類中的事件可基於任何有效委托類型(甚至是可返回值的委托),但是,通常建議您使用 EventHandler 讓事件基於 .NET 模式,如下面的示例所示。
1.(如果不需要與事件一起發送自定義數據,請跳過此步驟,進入步驟 3a。)在發行者類和訂閱方類均可看見的范圍中聲明自定義數據的類。然後添加保留您的自定義事件數據所需的成員。
1 class MyEventArgs : EventArgs
2 {
3 public string Message { get; private set; }
4
5 public MyEventArgs(string message)
6 {
7 Message = message;
8 }
9 }
2.(如果您使用的是 EventHandler<TEventArgs> 的泛型版本,請跳過此步驟。)在發布類中聲明一個委托。 為它指定以 EventHandler 結尾的名稱。 第二個參數指定自定義 EventArgs 類型。
delegate void MyEventHandler(object sender, MyEventArgs args);
3.使用以下任一步驟,在發布類中聲明事件。
(1)如果沒有自定義 EventArgs 類,事件類型就是非泛型 EventHandler 委托。無需聲明委托,因為它已在創建 C# 項目時包含的 System 命名空間中進行了聲明。將以下代碼添加到發行者類中。
public event EventHandler MyEvent;
(2)如果使用的是 EventHandler 的非泛型版本,並且您有一個由 EventArgs 派生的自定義類,請在發布類中聲明您的事件,並且將來自步驟 2 的委托用作類型。
public event MyEventHandler MyEvent;
(3)如果使用的是泛型版本,則不需要自定義委托。相反,在發行者類中,您應將事件類型指定為 EventHandler<MyEventArgs>,將尖括號中的內容替換為自己的類的名稱。
public event EventHandler<MyEventArgs> MyEvent;
下面的示例通過將自定義的 MyEventArgs 類和 EventHandler<TEventArgs> 進行演示:
This is MyEventArgs.cs //事件參數
1 /// <summary>
2 /// 事件參數
3 /// </summary>
4 /// <remarks>一個自定義的類:自定義事件的參數</remarks>
5 class MyEventArgs : EventArgs
6 {
7 public string Message { get; }
8
9 public MyEventArgs(string message)
10 {
11 Message = message;
12 }
13 }
This is Publisher.cs //發布者
1 /// <summary>
2 /// 事件發布者
3 /// </summary>
4 class Publisher
5 {
6 //聲明一個泛型事件
7 public event EventHandler<MyEventArgs> MyEvent;
8
9 public void Publish()
10 {
11 Console.WriteLine("Publis is starting");
12
13 //你可以在事件觸發前寫些代碼
14
15 OnMyEvent(new MyEventArgs(DateTime.Now.ToString()));
16 }
17
18 /// <summary>
19 /// 觸發事件
20 /// </summary>
21 /// <param name="args"></param>
22 /// <remarks>虛方法,允許子類重寫調用行為</remarks>
23 protected virtual void OnMyEvent(MyEventArgs args)
24 {
25 //只有在事件訂閱時(!= null),才觸發事件
26 MyEvent?.Invoke(this, args);
27 }
28 }
This is Subscriber.cs //訂閱者
1 /// <summary>
2 /// 訂閱者
3 /// </summary>
4 class Subscriber
5 {
6 public Guid Guid { get; }
7
8 public Subscriber(Publisher publisher)
9 {
10 Guid = Guid.NewGuid();
11 //使用 C# 2 的語法進行訂閱
12 publisher.MyEvent += Publisher_MyEvent;
13 }
14
15 /// <summary>
16 /// 事件處理程序
17 /// </summary>
18 /// <param name="sender"></param>
19 /// <param name="args"></param>
20 private void Publisher_MyEvent(object sender, MyEventArgs args)
21 {
22 Console.WriteLine($" Message is {args.Message}, Guid is {Guid}.");
23 }
24 }
This is Program.cs //控制台,用於啟動
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 var publisher = new Publisher();
6 var subscriber1 = new Subscriber(publisher);
7 var subscriber2 = new Subscriber(publisher);
8
9 //觸發事件
10 publisher.Publish();
11
12 Console.WriteLine("OK!");
13 Console.Read();
14 }
15 }
四、實現自定義事件訪問器
事件是特殊類型的多路廣播委托,只能從聲明它的類中調用。客戶端代碼通過提供對應在引發事件時調用的方法的引用來訂閱事件。這些方法通過事件訪問器添加到委托的調用列表中,事件訪問器類似於屬性訪問器,不同之處在於事件訪問器被命名為 add 和 remove。在大多數情況下都不需要提供自定義的事件訪問器。如果您在代碼中沒有提供自定義的事件訪問器,編譯器會自動添加事件訪問器。但在某些情況下,您可能需要提供自定義行為。示例如下:
1 class MyClass
2 {
3 /// <summary>
4 /// 鎖
5 /// </summary>
6 private static object Locker = new object();
7
8 /// <summary>
9 /// 接口
10 /// </summary>
11 public interface IMyEvent
12 {
13 event EventHandler OnCall;
14 }
15
16 public class MyEvent : IMyEvent
17 {
18 /// <summary>
19 /// 觸發前事件
20 /// </summary>
21 event EventHandler PreEvent;
22
23 public event EventHandler OnCall
24 {
25 add
26 {
27 lock (Locker)
28 {
29 PreEvent += value;
30 }
31 }
32 remove
33 {
34 lock (Locker)
35 {
36 PreEvent += value;
37 }
38 }
39 }
40 }
41 }
《C# 知識回顧 - 序列化》
《C# 知識回顧 - 表達式樹 Expression Trees》
《C# 知識回顧 - 特性 Attribute》、《剖析 AssemblyInfo.cs - 了解常用的特性 Attribute》《C# 知識回顧 - 委托 delegate》、《C# 知識回顧 - 委托 delegate (續)》
《C# 知識回顧 - 事件入門》
【參考】微軟官方文檔