[C#]使用委派(Delegate)與事件(Event) - HackMD
文章推薦指數: 80 %
[C#]使用委派(Delegate)與事件(Event) ###### tags: `C#` `Programming` :::spoiler Log - 2019/05/01 version.
Published
LinkedwithGitHub
Like4
Bookmark
Subscribe
#[C#]使用委派(Delegate)與事件(Event)
######tags:`C#``Programming`
:::spoilerLog
-2019/05/01version1
-2022/01/19更新錯誤範例與補充用法
:::
##委派(Delegate)
-委派是一種將方法(method)**安全封裝**的類別,描述需封裝函數的**參數型別**和**回傳型別**
-可以使用`+`、`+=`、`-`、`-=`,將方法加入至委派的清單中
-引動(Invoke)委派時,委派會依序呼叫清單內的方法,並將傳入委派的參數傳入各方法中
-欲將委派物件的清單清空時,直接使委派指派為`null`即可
-`22/01/19`補充:可以使用`=`賦值運算子,使用的話跟賦值一樣會把前一個值蓋過去。
###基本使用
```C#=
/*
*delegate(回傳型別)[委派類別](參數型別..)
*宣告一個委派類別-ArithmeticExpr
*傳入方法(method)的回傳型別為void,參數型別為兩個int
*/
publicdelegatevoidArithmeticExpr(inta,intb);
//顯示出該方法的名稱與a+b的結果
publicstaticvoidAdd(inta,intb)
{
Console.WriteLine("Method:[Add]hasbeencalled.\nTheansweris:"+(a+b).ToString()+"\n");
}
//顯示出該方法的名稱與a-b的結果
publicstaticvoidSub(inta,intb)
{
Console.WriteLine("Method:[Sub]hasbeencalled.\nTheansweris:"+(a-b).ToString()+"\n");
}
//顯示出該方法的名稱與a*b的結果
publicstaticvoidMultiply(inta,intb)
{
Console.WriteLine("Method:[Multiply]hasbeencalled.\nTheansweris:"+(a*b).ToString()+"\n");
}
staticvoidMain(string[]args)
{
//宣告三個委派
ArithmeticExprar1,ar2,ar3;
/*******************Section1*******************/
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方法
/*******************Section2*******************/
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
/*******************Section3*******************/
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();
}
```
###像物件一樣對待它...
這個方法目前不知道可以應用在哪裡,但可以這樣使用。
使用`System.MulticastDelegate.GetInvocationList()`可以獲得委派的方法清單,下列的Code可以將`ar2`的方法清單內方法印出來
```C#=
Delegate[]delArray=ar2.GetInvocationList();
for(inti=0;i
-在定義事件(Event)之前,必須先定義委派(delegate)的型別,表示事件發生時,**呼叫其他類別或物件的函數**。
-**只能**使用`+=`和`-=`來註冊或取消註冊事件。
>**注意:當註冊該事件的類別或物件毀滅時,請務必許消註冊事件。
**
**例子:**
-在**A物件**發生某件事情時,同時改變**B物件**和**C物件**的狀態,例如:在**按下SPACE按鍵時**,**B物件**和**C物件**會同時回應**A物件**,直到回應五次。
>Main
```C#=
classProgram
{
staticvoidMain(string[]args)
{
ObjectAobjectA=newObjectA("Kawaii",5);
ObjectBobjectB=newObjectB();
ObjectCobjectC=newObjectC();
objectA.ObjAnounceEvent+=objectB.ObjBGreeting;
objectA.ObjAnounceEvent+=objectC.ObjCGreeting;
while(Console.ReadKey().Key==ConsoleKey.Spacebar)
{
objectA.OnObjAEventBeenCalled();
}
}
}
```
>ObjectA
```C#=
classObjectA
{
publicstringName{get;set;}
publicintCallCountThreshold{get;set;}
privateint_callCount;
publicdelegatevoidObjADelegate(ObjectAobjectA);
publiceventObjADelegateObjAnounceEvent;
publicObjectA(stringname,intcallCountThreshold)
{
Name=name;
CallCountThreshold=callCountThreshold;
_callCount=0;
}
publicvoidOnObjAEventBeenCalled()
{
_callCount+=1;
if(_callCount>CallCountThreshold)
{
ObjAnounceEvent=null;
Console.WriteLine("Cleartheinvocationlist.");
Environment.Exit(0);
}
ObjAnounceEvent?.Invoke(this);
}
}
```
>ObjectB
```C#=
classObjectB
{
publicvoidObjBGreeting(ObjectAobjectA)
{
Console.WriteLine("Hello,"+objectA.Name+".ThisisObjectB.");
}
}
```
>ObjectC
```C#=
classObjectC
{
publicvoidObjCGreeting(ObjectAobjectA)
{
Console.WriteLine("Iloveyou,"+objectA.Name+".ThisisObjectC.");
}
}
```
##.NET
**.NET**中有定義**泛型**的委派類別
-**System.Action**:回傳值為void,可以給予0到16不等的參數值。
-**System.Func**:如果需要回傳值可以用Func,跟Action一樣,可以給予0到16不等的參數值,最後一個參數為**回傳值**的型別。
例如在上面介紹委派的例子可以這樣改寫:
```C#=
//使用內建的Func進行改寫,最後一個泛型類別為回傳值
publicSystem.Action
**首先**來談談參數列(args),兩種EventHandler都有名叫`sender`的參數,顧名思義,該參數指的是發起事件的物件或類別。
`e`則是需要傳遞的事件資料。
這樣寫的意義在於統一了事件委派的規則,讓程式碼易讀且好維護。
在傳遞事件的資料時,可以創建一個`class`繼承`EventArgs`,這樣就可把我們想傳遞的訊息往創建的`class`裏頭塞了。
**問題來了**,**如果沒有想傳遞的事件資料呢?**這時可以使用`System.EventArgs.Empty`,這樣已表示沒有需要傳遞的事件資料了。
接下來,我們依照上面給的情境題進行改寫:
>Main
```C#=
classProgram
{
staticvoidMain(string[]args)
{
ObjectAobjectA=newObjectA("Kawaii",5);
ObjectBobjectB=newObjectB();
ObjectCobjectC=newObjectC();
objectA.ObjAnounceEvent+=objectB.ObjBGreeting;
objectA.ObjAnounceEvent+=objectC.ObjCGreeting;
while(Console.ReadKey().Key==ConsoleKey.Spacebar)
{
objectA.OnObjAEventBeenCalled();
}
}
}
```
>ObjectA
```C#=
classObjectA
{
publicstringName{get;set;}
publicintCallCountThreshold{get;set;}
publicintCallCount;
publicEventHandler
延伸文章資訊
- 1C#事件:Event - C语言中文网
在C# 语言中,Windows 应用程序、 ASP.NET 网站程序等类型的程序都离不开事件的应用。 事件是一种引用类型,实际上也是一种特殊的委托。 通常,每一个事件的发生都会 ...
- 2C# 事件(Event) | 菜鸟教程
C# 事件(Event) 事件(Event) 基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些提示信息,如系统生成的通知。应用程序需要在事件发生时响应事件。
- 3C# 事件(下) – 加上event關鍵字 - iT 邦幫忙
加上event後,C#編譯器就會禁止外部來執行這個委派。 https://ithelp.ithome.com.tw/upload/images/. 觸發事件時~. 當要執行委派, ...
- 4[C#]使用委派(Delegate)與事件(Event) - HackMD
[C#]使用委派(Delegate)與事件(Event) ###### tags: `C#` `Programming` :::spoiler Log - 2019/05/01 version.
- 5[C#語法] 事件(Event)使用簡單三步| 誠的碼記本 - - 點部落
用C#自訂事件簡單作法. ... 委派 delegate void EventPrototype(object sender, EventArgs e); //宣告事件 public event...