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 /函數指標 類似