---------- 20181128 subject: delegate, event, EventArgs ---------- 摘要: Delegate與Event搭配使用 概念: 1. Delegate 翻譯為委派 或 代理函數. 類似C++中的指標函數. 2. Delegate是Data Type, 跟Sting, Int..等類型用法相同. 3. Event 關鍵字後銜接Delegate 變數. 4. Delegate 委派以 += 指定實體函數. 未指定前為null, 也可以multicast多重指定, 只要函數宣告簽章一致就可以. 5. Event原本就是delegate, 原型為public delegate void EventHandler( Object sender, EventArgs e ). 步驟: 若只需要代理函數, 不需要Event: 1. 宣告delegate資料型態, 指定函數宣告簽章. 2. 在物件中, 開放Property為delegate資料型態. 3. 依需求呼叫物件本身的(Property為delegate的資料型態). 4. 在New子物件時, 指定銜接到Property的實體函數. 以+=指定. 例如: Benz.OnDanger += new DangerEventHandler(TooFast); 步驟: 若需要以Event呼叫代理函數: 1. 宣告delegate資料型態, 指定函數宣告簽章. 2. 宣告Event, 指定事件發生時, 要呼叫的Delegate代理函數. 3. 觸發Event, 將會呼叫到Delegate代理函數. 4. 在New子物件時, 指定銜接到Event的實體函數. 以+=指定. 例如: Benz.OnDanger += new DangerEventHandler(TooFast); Delegate與Event的差別參考: 1. 只有透過加註event,該委派型別變數才能被當成介面(Interface)定義的一部分,這是一般的欄位(Field)變數無法辦到的,此點可算event關鍵字最主要的功能。 2. 另外還有一點很大差異,委派型別變數加上event後,就只能透過類別內部的程式碼呼叫,不允許外部直接執行之。以上述程式為例,執行boo.DeletateTest();是OK的,但boo.EventTest();光在編譯時就過不了關,會出現以下錯誤: The event 'ConsoleApplication1.Program.Boo.EventTest' can only appear on the left hand side of += or -= (except when used from within the type 'ConsoleApplication1.Program.Boo') 3. 相較於屬性(Property)有get; set;,加上event後,我們可以實作add; remove;,可在呼叫端加掛或移除事件時執行自訂邏輯。 4. 在.NET Framework裡,所有宣告event關鍵字的事件,應遵循someEvent(object source, EventArgs e)的參數慣例,但自行開發時的事件,編譯器或IDE倒不會強制檢查就是了。 ---------- System.EventHandler: this.timerWarnFlash.Tick += new System.EventHandler(this.timerWarnFlash_Tick_1); ---------- Car.cs 示範 delegate + event 基本的用法 using System; using System.Collections.Generic; using System.Text; namespace ConsoleBase { // step 1: 宣告delegate. // delegate有2個主要作用: 1 限制函數簽章. 2 銜接event語法. // Windows習慣上delegate命名為(EventHandler或Callback結尾). // 語法為public delegate [函數宣告簽章]. public delegate void DangerEventHandler(int iSpeed); public class Car { int miSpeed; // step 2: 宣告event. // Windows習慣上Event命名為On開頭. // 語法為 public event [delegateEventHandler] [OnEventName] public event DangerEventHandler OnDanger; public int Speed { get { return miSpeed; } set { if (value > 120) { // step 3: 觸發event // 若已指定Event對應的實體函數, 則呼叫event. // 指定Event對應實體函數的語法為Beetle.OnDanger += Beetle_OnDanger; if (OnDanger != null) OnDanger(value); } miSpeed = value; } } } } ---------- CarAndEventArgs.cs 示範 delegate + event + EventArgs 基本的用法 using System; using System.Collections.Generic; using System.Text; namespace ConsoleBase { public class CarAndEventArgs { int miSpeed; // step 1: 宣告delegate. // delegate有2個主要作用: 1 限制函數簽章. 2 銜接event語法. // Windows習慣上delegate命名為EventHandler結尾. // 語法為public delegate [函數宣告簽章]. public delegate void SlowEventHandler(object sender, DataEventArgs e); // step 2: 宣告event. // Windows習慣上Event命名為On開頭. // 語法為 public event [delegateEventHandler] [OnEventName] public event SlowEventHandler OnSlowData; public int Speed { get { return miSpeed; } set { if (value < 60) { // step 3: 觸發event // 若已指定Event對應的實體函數, 則呼叫event. // 指定Event對應實體函數的語法為Beetle.OnSlowData += Beetle_OnSlowData; if (OnSlowData != null) { // step 4. 準備EventArgs的資料. DataEventArgs a1 = new DataEventArgs("速度最少60公里", value); OnSlowData(this, a1); } } miSpeed = value; } } } } ---------- DataEventArgs.cs using System; using System.Collections.Generic; using System.Text; namespace ConsoleBase { // Event呼叫時傳送的物件封裝. public class DataEventArgs : EventArgs { // 視需要包裝在event發生時要傳送的data(或客製化物件) public int mi1; public string ms1; public DataEventArgs(string s1, int i1) { ms1 = s1; mi1 = i1; } } } ---------- Program.cs using System; using System.Collections.Generic; using System.Text; namespace ConsoleBase { class Program { static void Main(string[] args) { Car Benz = new Car(); // A1. 指定事件的處理函數 Benz.OnDanger += new DangerEventHandler(TooFast); // 自行指定EventHandler Benz.Speed = 150; CarAndEventArgs Beetle = new CarAndEventArgs(); // B1. 指定事件的處理函數. 使用IDE自動產生. Beetle.OnSlowData += Beetle_OnSlowData; // 使用IDE自動指定EventHandler Beetle.Speed = 20; Console.WriteLine("Pressed Enter to continue."); Console.ReadLine(); } static void Beetle_OnSlowData(object sender, DataEventArgs e) { Console.WriteLine("慢速警告. 目前時速數={0}公里. {1}, sender={2}.", e.mi1, e.ms1, sender.ToString()); } static void TooFast(int iSpeed) { Console.WriteLine("超速警告. 目前時速數={0}公里.", iSpeed); } } }