事件(event)和委託(delegate)的綜合運用例項(WinForm控制元件事件執行流程和原理),和EventHandler委託
阿新 • • 發佈:2018-11-15
前言
關於委託前面已經寫了:
C#中委託(delegate)和多播委託的理解
Action和Func泛型委託,委託,泛型,匿名函式,Lambda表示式的綜合使用
其實初衷是為了解WinFrom中的控制元件或者COM元件中的各種事件執行的機制,裡面涉及的知識整理起來還是比較多的,各種型別的委託,事件,和泛型等。
這裡先整理事件(event),然後通過幾個例項瞭解架構中的事件從啟用到呼叫回撥函式的具體過程。
事件(event
)
事件的官方文件
事件基於委託,為委託提供了一種釋出/訂閱機制,
- 可將事件理解為一種特殊用途的委託,或者說是受限制的委託,是委託一種特殊應用;
- 事件裡面其實就兩個方法(即
add_event()
和remove_event()
)和一個私有的委託變數,這兩個方法裡面分別是對這個私有的委託變數進行的合併和移除,當呼叫事件的 += 時其實是呼叫的事件裡面的add_event()
方法,同樣 -= 呼叫的是remove_event()
方法; - 事件只能夠從物件外部增加新的響應方法和刪除已知的響應方法,而不能主動去觸發事件和獲取其他註冊的響應方法等資訊。如果使用公有的delegate則不能做這些限制,也就是說事件對委託做了限制,使委託使用起來更加方便。
可以以下面這個圖來理解:
從圖中我們可以理解什麼是釋出者和訂閱者,
且為什麼說事件是特殊用途的委託,是受限制的委託?
限制一:不同於委託和類同級(可以在類的外邊或者裡邊宣告),事件只能在類中宣告;
限制二:事件只能在宣告該事件的類(釋出者)中被呼叫,在訂閱者中只能通過 += 和 -= 繫結或者移除回撥方法;
標準的事件模式的步驟:
- 宣告委託;
這裡我們沒有定義委託,因為我們採用的是框架定義好的EventHandler<TEventArgs>
泛型委託,如何理解這種委託?
EventHandler<TEventArgs>
泛型委託:
https://docs.microsoft.com/zh-cn/dotnet/api/system.eventhandler-1?view=netframework-4.7.2
是一個預定義的委託,表示生成資料的事件的事件處理程式方法。 使用的優點EventHandler<TEventArgs>
是你不需要程式碼自定義委託,如果您的活動生成的事件資料。 只需作為泛型引數提供的事件資料物件的型別。
它類似於前面提到的Action<T>
和Func<T>
泛型委託,我們呼叫時只需要傳入一個派生自基類EventArgs
的引數,框架會自動生成預設委託為可以封裝返回值為空,一個引數為object型別;另一個引數為我們傳入的TEventArgs
引數。
[Serializable]
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
- 宣告事件;
事件由釋出者宣告,且只能宣告在類的內部;
public event EventHandler<carInfoEventArgs> NewCarInfo;//步驟二:宣告事件
- 構建回撥方法;
在訂閱者中構建回撥函式newCarIsHere
- 繫結回撥方法;
在帶哦也是通過 +=繫結回撥方法,-= 解綁;可以通過傳入可以被委託封裝的方法或者該委託的例項;
//dealer.NewCarInfo += bmw.newCarIsHere;//步驟四:繫結回撥方法,方法一
dealer.NewCarInfo += new EventHandler<carInfoEventArgs>(bmw.newCarIsHere);//步驟四:繫結回撥方法,方法二
- 構建觸發事件的方法;
觸發事件的方法定義在釋出者中,觸發之前檢查事件繫結的回撥函式是否為空;事件的呼叫類似與沒有返回值的方法的呼叫,傳入事件引數:事件源和事件相關資訊; - 呼叫觸發事件的方法。
namespace EventAndDelegate
{
/// <summary>
///
/// </summary>
/// <param name="?"></param>
/// <returns></returns>
class Program
{
static void Main()
{
carDealer dealer = new carDealer();
consumer my= new consumer("My");
//dealer.NewCarInfo += my.newCarIsHere;//步驟四:繫結回撥方法,方法一
dealer.NewCarInfo += new EventHandler<carInfoEventArgs>(my.newCarIsHere);//步驟四:繫結回撥方法,方法二
dealer.NewCar("BMW");//步驟六:呼叫觸發事件的方法
consumer you= new consumer("YOU");
dealer.NewCarInfo += you.newCarIsHere;
dealer.NewCar("AMG");
dealer.NewCarInfo -= my.newCarIsHere;
dealer.NewCar("AMG");
Console.ReadLine();
}
}
public delegate void delegateMOT(int x,int y);
/// <summary>
/// 事件釋出者 carDealer
/// </summary>
public class carDealer
{
public event EventHandler<carInfoEventArgs> NewCarInfo;//步驟二:宣告事件
public void NewCar(string car)//步驟五:構建觸發事件的方法
{
Console.WriteLine("carDealer,new car {0}", car);
if (NewCarInfo != null)//判斷委託是否為空,如果沒有訂閱程式,則為空
{
NewCarInfo(this, new carInfoEventArgs(car));//觸發事件傳入事件的傳送者和和事件的相關資訊
}
}
}
/// <summary>
/// 事件訂閱者 consumer
/// </summary>
public class consumer
{
private string name;
public consumer(string name)
{
this.name = name;
}
/// <summary>
/// 步驟三:構建回撥方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void newCarIsHere(object sender,carInfoEventArgs e)
{
Console.WriteLine("{0}:car {1} is new",name,e.car);
}
}
/// <summary>
/// carInfoEventArgs 為事件引數,是一個只包含car屬性的物件,注意繼承EventArgs
/// </summary>
public class carInfoEventArgs : EventArgs
{
public carInfoEventArgs(string car)
{
this.car = car;
}
public string car { get; private set; }
}
}
結果:
carDealer,new car BMW
My:car BMW is new
carDealer,new car AMG
My:car AMG is new
YOU:car AMG is new
carDealer,new car AMG
YOU:car AMG is new