C#-Event事件和發布者/訂閱者模式


很多程序都有這樣的一個需求,當一個特定的事件發生時,程序的其他部分能夠得到通知,並且需要做一些事情。這個時候就需要事件了。

發布者/訂閱者模式

發布者/訂閱者模式(publisher/subscriber pattern)就是滿足這種需求,設計模式中也叫觀察者模式。發布者存儲一個方法集合,並且提供一個注冊方法,讓訂閱者把自己的方法注冊進去,這樣在事件發生的時候,發布者可以調用注冊到存儲集合中的所有方法。

有以下要點:

  • 發布者(publisher)
  • 訂閱者(subscriber)
  • 事件處理程序(event handler)訂閱者注冊到發布者的方法
  • 觸發(raise)事件 當事件被調用(invoke)或觸發(fire)時,所有注冊的事件處理程序會被依次調用

使用步驟

  1. 聲明委托類型
  2. 聲明事件
  3. 事件處理程序聲明
  4. 訂閱事件/注冊事件
  5. 觸發事件(調用事件)

代碼示例:

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM