C#中的delegate(委託)和event(事件)
C#中的delegate(委託)和event(事件)
資料來源:
http://www.itivy.com/ivy/archive/2011/8/5/csharp-delegate-and-event.html
https://www.twblogs.net/a/5b7cc3582b71770a43dc786b
https://hackmd.io/@BKLiang/csharp_delegate_event
孩子,C/C++總學過吧,如果你學得不像我那麼差的話,函數指針總用過吧,就算沒用過總聽說過吧,嗯,大膽的告訴你,你完全可以把delegate理解成C中的函數指針,它允許你傳遞一個類A的方法m給另一個類B的對象,使得類B的對象能夠調用這個方法m,說白了就是可以把方法當作參數傳遞。
不過delegate和函數指針還是有點區別的,delegate有許多函數指針不具備的優點:
首先,函數指針只能指向靜態函數,而delegate既可以引用靜態函數,又可以引用非靜態成員函數。
在引用非靜態成員函數時,delegate不但保存了對此函數入口指針的引用,而且還保存了調用此函數的類實例的引用。
其次,與函數指針相比,delegate是面向對象、類型安全、可靠的受控(managed)對象。
也就是說,runtime能夠保證delegate指向一個有效的方法, 你無須擔心delegate會指向無效地址或者越界地址。
有什麼能比舉個例子更能說明問題呢,代碼纔是硬道理,來吧,看幾個例子吧:
範例01.
public class DelegateTest { // 聲明delegate對象 public delegate void CompareDelegate(int a,int b); // 欲傳遞的方法,它與CompareDelegate具有相同的參數和返回值類型 public static void Compare(int a,int b) { Console.WriteLine((a>b).ToString()); } public static void Main() { // 創建delegate對象 CompareDelegate cd = new CompareDelegate(DelegateTest.Compare); // 調用delegate cd(1,2); } }
範例02.
public delegate void MyTestDelegate(int i); public class Program { public static void Main() { //創建delegate ReceiveDelegateArgsFunc(new MyTestDelegate(DelegateFunction)); } //這個方法接收一個delegate類型的參數,也就是接收一個函數作爲參數 public static void ReceiveDelegateArgsFunc(MyTestDelegate func) { func(21); } //欲傳遞的方法 public static void DelegateFunction(int i) { System.Console.WriteLine("傳過來的參數爲: {0}.", i); } }
範例03.
//這裏自定義一個EventArgs,因爲我想知道Clicker public class ButtonClickArgs : EventArgs { public string Clicker; } public class MyButton { //定義一個delegate委託 public delegate void ClickHandler(object sender, ButtonClickArgs e); //定義事件,類型爲上面定義的ClickHandler委託 public event ClickHandler OnClick; public void Click() { //...觸發之前可能做了n多操作 //..... //這時觸發Click事件,並傳入參數Clicker爲本博主ivy OnClick(this, new ButtonClickArgs() { Clicker = "ivy" }); } } public class Program { public static void Main() { MyButton btn = new MyButton(); //註冊事件,把btn_OnClick方法壓入事件隊列, //可以+=多個,這裏簡單點就壓入一個吧。 btn.OnClick += new MyButton.ClickHandler(btn_OnClick); } //怎麼看到這個函數很熟悉吧,就是你原來雙擊button自動產生的代碼 public static void btn_OnClick(object sender, ButtonClickArgs e) { Console.WriteLine("真賤,我居然被ivy點擊了!"); } }
範例04.
/* * delegate (回傳型別) [委派類別] (參數型別..) * 宣告一個委派類別 - ArithmeticExpr * 傳入方法(method)的回傳型別為void,參數型別為兩個int */ public delegate void ArithmeticExpr(int a, int b); // 顯示出該方法的名稱與 a + b 的結果 public static void Add(int a, int b) { Console.WriteLine("Method:[Add] has been called.\nThe answer is: " + (a + b).ToString() + "\n"); } // 顯示出該方法的名稱與 a - b 的結果 public static void Sub(int a, int b) { Console.WriteLine("Method:[Sub] has been called.\nThe answer is: " + (a - b).ToString() + "\n"); } // 顯示出該方法的名稱與 a * b 的結果 public static void Multiply(int a, int b) { Console.WriteLine("Method:[Multiply] has been called.\nThe answer is: " + (a * b).ToString() + "\n"); } static void Main(string[] args) { // 宣告三個委派 ArithmeticExpr ar1, ar2, ar3; /*******************Section 1*******************/ ar1 = Add; // 起始將Add指派給ar1 ar1(10, 7); // Invoke委派,呼叫Add並顯示出答案: 17 ar1 = Sub; // ar1指派為Sub ar1(10, 7); // Invoke委派,呼叫Sub並顯示出答案: 3 ar1.Invoke(10, 7); // 或是可以使用Invoke方法 /*******************Section 2*******************/ ar2 = Add; // 起始將Add指派給ar2 ar2(3, 41); // Invoke委派,呼叫Add並顯示出答案: 44 ar2 += Sub; // 將Sub方法加入ar2的方法清單中 // 現在方法清單中有Add和Sub (順序為: Add -> Sub) ar2(3, 41); // Invoke委派,首先呼叫Add,答案為44;而後呼叫Sub,答案為-38 /*******************Section 3*******************/ ar3 = Multiply; // 起始將Multiply指派給ar3 ar3 += Add; // 將Add方法加入ar3的方法清單中 // 現在方法清單中有Multiply和Add (順序為: Multiply -> Add) ar3 += Sub; // 將Sub方法加入ar3的方法清單中 // 現在方法清單中有Multiply、Add和Sub (順序為: Multiply -> Add -> Sub) ar3(67, 19); // Invoke委派,依序呼叫 Multiply、Add和Sub ar3 -= ar2; // 將Add、Sub移出方法清單 ar3(67, 19); // 再次Invoke委派,這次只有ar3被呼叫 ar3 = null; // 清空整個方法清單 ar3(18, 0); // ERROR ==> 方法清單已經被清空了,拋出例外 Console.ReadKey(); }
範例05.
class ObjectA { public string Name { get; set; } public int CallCountThreshold { get; set; } private int _callCount; public delegate void ObjADelegate(ObjectA objectA); public event ObjADelegate ObjAnounceEvent; public ObjectA(string name, int callCountThreshold) { Name = name; CallCountThreshold = callCountThreshold; _callCount = 0; } public void OnObjAEventBeenCalled() { _callCount += 1; if (_callCount > CallCountThreshold) { ObjAnounceEvent = null; Console.WriteLine("Clear the invocation list."); Environment.Exit(0); } ObjAnounceEvent?.Invoke(this); } } class ObjectB { public void ObjBGreeting(ObjectA objectA) { Console.WriteLine("Hello, " + objectA.Name + ". This is Object B."); } } class ObjectC { public void ObjCGreeting(ObjectA objectA) { Console.WriteLine("I love you, " + objectA.Name + ". This is Object C."); } } class Program { static void Main(string[] args) { ObjectA objectA = new ObjectA("Kawaii", 5); ObjectB objectB = new ObjectB(); ObjectC objectC = new ObjectC(); objectA.ObjAnounceEvent += objectB.ObjBGreeting; objectA.ObjAnounceEvent += objectC.ObjCGreeting; while (Console.ReadKey().Key == ConsoleKey.Spacebar) { objectA.OnObjAEventBeenCalled(); } } }
範例06.
// 使用內建的Func進行改寫,最後一個泛型類別為回傳值 public System.Action<int, int> ArithmeticExpr; // 顯示出該方法的名稱與 a + b 的結果 public static void Add(int a, int b) { Console.WriteLine("Method:[Add] has been called.\nThe answer is: " + (a + b).ToString() + "\n"); } // 顯示出該方法的名稱與 a - b 的結果 public static void Sub(int a, int b) { Console.WriteLine("Method:[Sub] has been called.\nThe answer is: " + (a - b).ToString() + "\n"); } // 顯示出該方法的名稱與 a * b 的結果 public static void Multiply(int a, int b) { Console.WriteLine("Method:[Multiply] has been called.\nThe answer is: " + (a * b).ToString() + "\n"); } /* System.EventHandler: 內部定義兩種EventHandler public delegate void EventHandler(object sender, EventArgs e); public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e); */
範例07.
class ObjectA { public string Name { get; set; } public int CallCountThreshold { get; set; } public int CallCount; public EventHandler<ObjectAEventArgs> ObjAnounceEvent; public ObjectA(string name, int callCountThreshold) { Name = name; CallCountThreshold = callCountThreshold; CallCount = 0; } public void OnObjAEventBeenCalled() { CallCount += 1; if (CallCount > CallCountThreshold) { ObjAnounceEvent = null; Console.WriteLine("Clear the invocation list."); Environment.Exit(0); } ObjAnounceEvent?.Invoke(this, new ObjectAEventArgs(Name)); } } class ObjectB { public void ObjBGreeting(object sender, ObjectAEventArgs args) { Console.WriteLine("Hello, " + args.Name + ". This is Object B."); } } class ObjectC { public void ObjCGreeting(object sender, ObjectAEventArgs args) { Console.WriteLine("I love you, " + args.Name + ". This is Object C."); } } class ObjectAEventArgs : EventArgs { public ObjectAEventArgs(string name) { Name = name; } public string Name { get; set; } } class Program { static void Main(string[] args) { ObjectA objectA = new ObjectA("Kawaii", 5); ObjectB objectB = new ObjectB(); ObjectC objectC = new ObjectC(); objectA.ObjAnounceEvent += objectB.ObjBGreeting; objectA.ObjAnounceEvent += objectC.ObjCGreeting; while (Console.ReadKey().Key == ConsoleKey.Spacebar) { objectA.OnObjAEventBeenCalled(); } } }
One thought on “C#中的delegate(委託)和event(事件)”
C/C++ callback function /函數指標 類似