為什麼C#中的event 的定義要用到委派delegate?
在官方文件的介紹中有這麼一段描述:事件是一種特殊的多播委派。
那也就是說事件Event其實是委派Delegate的一種封裝,事件的底層是委派。那好,為了證明這一點我們只用委派去實現整個事件與觸發。
什麼是委派?
比如,你外賣到了你不想動,叫你室友幫你去拿,然後你室友就幫你拿了。這就是委派(委託),委託別人幫你做一件事情。
- 定義:
delegate - 語法:
權限修飾符 delegate 傳回值 委派名稱 (參數類型 參數);
特別注意:被委派的方法有無回傳值、傳回值類型、有無參數、參數類型要與委派一致
1
2
3
4
delegate void MyDelegate(); //宣告一個委派,可以指向 無參無傳值 的方法
MyDelegate test = One; //實體化委派,並指向一個方法(指向one方法)
void One() { Console.WriteLine("one方法"); } //給委派用的方法
委派中的多重傳送委派
「多重傳送委派(Multicast-Delegate)」說穿了,你把實體化好的委派看成是一個隊列或集合,+=看成是執行Add方法,-=看成是執行Remove方法,但如果你直接用= 號那就相當於把這個方法 直接賦值給了委派物件,會造成前面的加入進委派物件的方法被覆寫。
但是,委派有一個弊端,它可以使用「=」將所有已經訂閱的取消,只保留=後的這一個訂閱。
為了解決這個弊端,事件event應運而生。
因為事件(
event)不能用=,它只能+=,-=,不能直接用=。
event在定義類別中(發布者)是可以直接=的,但是在其他類別中(訂閱者)就只能+=-=了,也就是說發布者發布一個事件後,訂閱者針對他只能進行自身的訂閱和取消。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
internal class Program
{
//宣告一個委派,可以指向 無參無傳值 的方法
delegate void MyDelegate();
static void Main(string[] args)
{
//實體化委派,並指向一個方法
MyDelegate test = One; //指向one方法
test += Two; //再把Two方法加入
test += Three; //再把Three方法加入
test += Four; //再把Four方法加入
test -= Three; //移除Three方法
//執行委派
test();
/* 執行結果:
one方法
Two方法
Four方法
*/
Console.WriteLine("===");
test = Three; //如果你直接用 = 號, 那就相當於把這個方法直接賦值給了委派對象
test();
/* 執行結果:
Four方法
所以這也是 委派有一個弊端,它可以使用「=」將所有已經訂閱的取消,只保留=後的這一個訂閱。
為了解決這個弊端,事件event應運而生。(因為事件不能用 =,只能用+= -=)
event在定義類別中(發布者)是可以直接=的,但是在其他類別中(訂閱者)就只能+= -=了,
也就是說發布者發布一個事件後,訂閱者針對他只能進行自身的訂閱和取消。
*/
}
static void One() { Console.WriteLine("one方法"); }
static void Two() { Console.WriteLine("Two方法"); }
static void Three() { Console.WriteLine("Three方法"); }
static void Four() { Console.WriteLine("Four方法"); }
}
範例
假設一群人在宿舍,有人要下樓去買午餐,其他人也跟著請他買,並寫在清單上。
- 先定義兩個類別:工具人
ToolMan、人Man - 使用委派
delegate(委派就是:有一件事情,我不親自去做,而是交給別人來做)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
internal class Program
{
static void Main(string[] args)
{
Man me = new("Riv");
Man man1 = new("張三");
Man man2 = new("李四");
ToolMan toolMan = new("小菜");
//每個人把要做的事情,都寫在清單上(委派加入多個方法,也叫做多播委派)
//交由工具人去做
toolMan.buyDelegate += me.TakeFood;
toolMan.buyDelegate += man1.TakeFood;
toolMan.buyDelegate += man2.TakeFood;
toolMan.buyDelegate += man2.TakePackage;
//工具人去買東西了
toolMan.Buy(); //Buy()方法裡 會觸發執行委派
}
}
//Man 類別
class Man
{
public Man(string name)
{
this.Name = name;
}
public string Name { get;}
public void TakeFood()
{
Console.WriteLine($"買食物 ({this.Name}的)");
}
public void TakePackage()
{
Console.WriteLine($"拿快遞 ({this.Name}的)");
}
}
//工具人類別
//定義委派,目的為:買東西觸發
delegate void BuyDelegate();
class ToolMan
{
//宣告委派
public BuyDelegate? buyDelegate;
public ToolMan(string name)
{
this.Name = name;
}
public string Name { get; }
//買東西方法
public void Buy()
{
Console.WriteLine($"工具人{this.Name}去買東西了");
//如果委派不為空,就執行委派方法
if (buyDelegate != null) buyDelegate();
}
}
執行結果:
1
2
3
4
5
工具人小菜去買東西了
買食物 (Riv的)
買食物 (張三的)
買食物 (李四的)
拿快遞 (李四的)
什麼是事件?
在程式碼裡面,事件(event)代表一類事情或一個方法,一旦被觸發就會執行,事件與觸發器是一個整體。
1
2
3
4
//宣告一個委派
public delegate void MyDelegate(int x);
//宣告一個事件
public event MyDelegate emd; //將委派delegate加上event改成事件
將委派delegate加上event改成事件
事件和委派的區別?
事件Event本質上還是一個委派,那它跟委派delegate有什麼區別呢?
區別在於你加了event,你再去訪問它時,它是受限制的,
這個事件event相當於受限制的委派,
這個限制就是:
- 你在外界這個委派只能增加方法
+=、減少方法=+,不能賦值=。 - 這個事件你只能在內部觸發,這個事件它在外部你是無法直接調用的。
1
2
3
4
5
6
7
8
9
10
//委派 可以 += =+ =
//如果沒有用 事件event,委派若誤用了=賦值,前面附加的方法都會被覆蓋,只剩me.TakeFood
toolMan.buyDelegate = me.TakeFood;
toolMan.buyDelegate();
//加上 事件 event:
//1. 在外界這個委派只能增加方法 +=、減少方法 =+,不能賦值 =
//toolMan.buyDelegate = me.TakeFood; //不能賦值 =
//2.這個事件你只能在內部觸發,這個事件它在外部你是無法直接調用的。
//toolMan.buyDelegate(); //這個事件它在外部你是無法直接調用的
這兩個限制,就是進行權限的限制,它是一個特殊的委派,是一個受限制的委派,它是一個進行權限的作用。
使用事件event
將委派delegate前面加上event改成事件
剛範例會有些問題:
- 如果把方法加入委派時,不小心把
+=寫成=,就變成賦值,其他的方法就不會觸發了。 - 如果不小心把直接調用工具人的委派方法,就變成工具人沒下樓,所有委派的方法都執行了。
1
2
3
4
5
//1. 如果把方法加入委派時,不小心把 += 寫成 =,就變成賦值,其他的方法就不會觸發了。
toolMan.buyDelegate = me.TakeFood;
//2.直接調用工具人的委派方法,就變成工具人沒下樓,所有委派的方法都執行了。
toolMan.buyDelegate();
範例(使用事件Event)
(將委派delegate加上event改成事件)
假設一群人在宿舍,有人要下樓去買午餐,其他人也跟著請他買,並寫在清單上。
- 先定義兩個類別:工具人
ToolMan、人Man - 使用事件
event(委派delegate前面加上event)
委派就是:有一件事情,我不親自去做,而是交給別人來做 事件就是:相當於受限制的委派
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
internal class Program
{
static void Main(string[] args)
{
Man me = new("Riv");
Man man1 = new("張三");
Man man2 = new("李四");
ToolMan toolMan = new("小菜");
//每個人把要做的事情,都寫在清單上(委派加入多個方法,也叫做多播委派)
//交由工具人去做
toolMan.buyDelegate += me.TakeFood;
toolMan.buyDelegate += man1.TakeFood;
toolMan.buyDelegate += man2.TakeFood;
toolMan.buyDelegate += man2.TakePackage;
//1.如果沒有用 事件event,委派若誤用了=賦值,前面附加的方法都會被覆蓋,只剩me.TakeFood
//toolMan.buyDelegate = me.TakeFood; //使用event,只能+=或-=,不能賦值=
//2.委派可以在外部調用方法,這樣會有問題的,
//所以要改成event, 讓它只能在工具人內部調用
//toolMan.buyDelegate();
//工具人去買東西了
toolMan.Buy(); //Buy方法裡
}
}
class Man
{
public Man(string name)
{
this.Name = name;
}
public string Name { get;}
public void TakeFood()
{
Console.WriteLine($"買食物 ({this.Name}的)");
}
public void TakePackage()
{
Console.WriteLine($"拿快遞 ({this.Name}的)");
}
}
//定義委派,目的為:買東西觸發
delegate void BuyDelegate();
class ToolMan
{
//宣告 event事件-受限制的委派(委派前面加上event)
public BuyDelegate? buyDelegate;
public ToolMan(string name)
{
this.Name = name;
}
public string Name { get; }
public void Buy()
{
Console.WriteLine($"工具人{this.Name}去買東西了");
//如果委派不為空,就執行委派方法
if (buyDelegate != null) buyDelegate();
}
}
執行結果:
1
2
3
4
5
工具人小菜去買東西了
買食物 (Riv的)
買食物 (張三的)
買食物 (李四的)
拿快遞 (李四的)
MSDN - 處理和引發事件
MSDN - 简单理解C#中的委托(delegate)与事件(event)
CSDN - 彻底弄懂C#中delegate、event、EventHandler、Action、Func的使用和区别
[C# 筆記] 工具人下樓問題(Delegate+Event) by R