Home [C# 筆記] 事件Event概念
Post
Cancel

[C# 筆記] 事件Event概念

事件(Event)

  • 事件是C#饋贈我們的禮物,是將委託的多播功能進行封裝後的工具類型。

這句話怎麼理解呢?其實事件本質上是一種多播的委託,

什麼是事件?

  • 一個對象在完成某個工作後,或者發生了某種操作後,需要通知其他對象,從而做出反應;發送出去的通知就是事件。

案例1:火箭發射-火箭飛了

比如說:火箭發射,這裡有一個Push按鈕、有一個火箭

當你按下這個按鈕之後,一按,這個火箭就開始起飛,是吧,那「按下按鈕」這件事呢,就是從而構成了一個實踐,對吧。

案例2:玩家AOE技能攻擊-敵人減血

比如說,在遊戲當中,你看中間的這樣一個玩家,他會釋放一種技能叫做AOE,AOE就是這個大範圍的廣範圍的攻擊,他這麼一打,是不是周邊的這些敵人都受到了他的攻擊啊,這種技能呢,可以理解成一種實踐。

怎麼說呢,你看,中間的玩家圍了一圈的敵人要攻擊玩家,這個時候呢,你搞一個AOE技能,然後呢,向所有的敵人發送了一個事件說:「哈哈,你在我們攻擊範圍之內,所以說呢,我要揍你了。」

這些敵人呢,收到「你要揍我」這個事件之後,好吧,我要幹嘛呢?集體減血啊,你看,啪啪啪都減了100血。

那玩家執行了AOE這樣一個技能操作之後,然後呢,向這些敵人所發送的這些消息啊,可以稱之為「事件 Event」。


AOE 技能如果不使用事件,應該怎麼寫呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
class Enemy {
    private int blood = 100; //血量
    public void MinusBlood(int attack) {
        blood -= attack;
    }
} 
class Player {
    public void DoAOE(Enemy[] enemies) {
        foreach(Enemy e in enemies) {
            e.MinusBlood(10);
        }
    }
}

這樣的寫法會有什麼樣的問題:

  • Enemy中的 MinusBlood()更改了名字,Player的程式碼也需要隨之更改。

這種情況下,我們就稱之為二者耦合了,Player 耦合於Enemy,當Enemy被改變了,Player就被迫於要更改,這樣的設計其實是不好的。

  • Player類中,顯式調用了Enemy類的方法,則Player的程式碼依賴Enemy類,構成了耦合關係

我們的目標是「解耦合」,讓玩家不再依賴於敵人,要怎麼實現呢?

AOE技能如何能夠「解耦合」呢?

Enemy 敵人

1
2
3
4
5
6
class Enemy {
    private int blood  = 100;
    public void MinusBlood(int attack) {
        blood -= attack;
    }
}

Player 玩家

1
2
3
4
5
6
7
8
9
10
class Player {
    public delegate void OnAttackDelegate(int attack);
    public OnAttackDelegate OnAttack = null;
    ......(一堆屬性)
    public void DoAOE() {
        if(OnAttack != null) {
            OnAttack(10);
        }
    }
}
  • Player 類裡面聲明了一個委託的方法類型,看這個類型是怎樣的呢?它要求這樣一個方法是傳入一個int類型的數值,返回的是一個void,就是沒有返回值。而這一類方法我們把它統稱為OnAttackDelegate
  • 隨後呢,咱們聲明了一個OnAttackDelegate類型的委託對象/物件叫做OnAttack,然後一開始等於null
  • 之後呢,咱們DoAOE()這個方法是這麼來寫的:如果OnAttack不等於null,就是咱們這個OnAttack委託,如果說已經被設置了一些委託進去了,那裡面已經有一個,或是多個方法被委託到他們這個OnAttack裡面了,那這個時候它就不是null了,那麼咱們就可以直接調用OnAttack這樣的委託,然後呢,把這個攻擊的數值傳進去。

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//玩家
Player player = new Player();

//敵人
Enemy e0 = new Enemy();
Enemy e1 = new Enemy();
Enemy e2 = new Enemy();

//委託的多播
//三個敵人物件的MinusBlood都被收錄到OnAttack委託裡
player.OnAttack += e0.MinusBlood;
player.OnAttack += e1.MinusBlood;
player.OnAttack += e2.MinusBlood;

//使用AOE技能
player.DoAOE();

實作

需求:

  1. Player Class: 能夠釋放AOE範圍攻擊技能,讓波及到的敵人統一減血10滴
  2. Enemy Class: 能夠對外提供減血的方法函數,供外界調用

耦合版本

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
namespace SeniorEvent
{
    //需求:
    // 1 Player Class: 能夠釋放AOE範圍攻擊技能,讓波及到的敵人統一減血10滴
    // 2 Enemy Class: 能夠對外提供減血的方法函數,供外界調用

    class Enemy {
        private int blood = 100;
        public void MinusBlood(int attack) {
            blood -= attack;
        }
    }

    //耦合版本
    class Player {
        public void DoAOE(Enemy[] enemies) {
            foreach (Enemy e in enemies) {
                e.MinusBlood(10);
            }
        }
    }

    internal class Program
    {
        static void Main(string[] args)
        {
            Player player = new Player();

            Enemy e0 = new Enemy();
            Enemy e1 = new Enemy();
            Enemy e2 = new Enemy();

            Enemy[] enemies = { e0, e1, e2 };
            player.DoAOE(enemies);
        }
    }
}

使用委託解耦合

Player 當中聲明委託類型,將需要調用的減血方法,在Player 類別外設給內部的委託

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
namespace SeniorEvent
{
    //需求:
    // 1 Player Class: 能夠釋放AOE範圍攻擊技能,讓波及到的敵人統一減血10滴
    // 2 Enemy Class: 能夠對外提供減血的方法函數,供外界調用

    //敵人
    class Enemy {
        private int blood = 100;
        public void MinusBlood(int attack) {
            Console.WriteLine("好疼,我是Enemy");
            blood -= attack;
        }
    }

    //非玩家角色
    class NPC {
        private int blood = 100;
        public void BeAttacked(int attack) {
            Console.WriteLine("好疼,我是NPC");
            blood -= attack;
        }
    }

    //使用委託解耦合
    //在 Player 當中聲明委託類型,將需要調用的減血方法,在 Player 類別外設給內部的委託
    class Player 
    {
        //聲明委託,用來規定減血的方法 應該符合怎麼樣的 方法簽名
        public delegate void OnAttackDelegate(int attack);
        //聲明OnAttackDelegate類型的委託
        public OnAttackDelegate OnAttack = null;

        public void DoAOE() {
            if (OnAttack != null) {
                OnAttack(10);
            }
        }
    }

    internal class Program
    {
        static void Main(string[] args)
        {
            Player player = new Player();

            Enemy e0 = new Enemy();
            Enemy e1 = new Enemy();
            Enemy e2 = new Enemy();

            NPC npc = new NPC();

            //player.OnAttack 是 Player內部甩出外界的一個委託
            player.OnAttack += e0.MinusBlood;
            player.OnAttack += e1.MinusBlood;
            player.OnAttack += e2.MinusBlood;
            player.OnAttack += npc.BeAttacked;

            player.DoAOE();

            Console.Read();
        }
    }
}
  • 玩家Player就不再依賴敵人Enemy,還可以跨種族的攻擊。
  • player.OnAttackPlayer內部甩出外界的一個「委託」,這個player要去攻擊別人的時候,會去調用這個OnAttack這個委託。
  • 於是乎,我可以把受到這個player攻擊的方法,把它加入到這個OnAttack裡面,這個含義

命名規則 「On什麼什麼東西」

比如說,Player這個類別裡面發生一些事情,發生事情之後,他要去做一些事,例如耍一些技能,他就需要去向外界做出一些通知,對吧。

所以說,這一類的「委託方法」,或者說這個「回調方法」,我們一般都叫「On什麼什麼東西」。

比如說,OnAttack其實是可以翻譯成:「在我攻擊的時候」,對吧。還有OnClick,這個是一般按鈕都會有的一個委託方法,那它翻譯過來就是,「在我被點擊的時候」。

放在專案裡面:

  • OnClick就是:在我被點擊的時候所調用的方法。
  • OnAttack在我被攻擊的時候所調用的方法集合。
1
2
3
4
 //OnAttack在我被攻擊的時候所調用的方法集合
 public OnAttackDelegate OnAttack = null;
 //OnClick在我被點擊的時候所調用的方法
 public OnClickDelegate OnClick = null;

發散思維

假設我攻擊敵人後,敵人發動被動技能-背刺,就是,你打我,我背上有穿上背刺穿甲,你這個拳打到我身上,我扎到你了,是不是你也得掉血啊。

也就是說,誰攻擊了我,那麼,攻擊我的那個人呢,也需要掉血啊, 這就是「背刺功能」。

你看,這個Player Class做了一個AOE技能,然後去調用了每一個MinusBlood()的方法,然後調用到這裡MinusBlood,敵人要先減血,因為敵人被 player 揍了嘛。

那如果敵人要發動被動技能-背刺,那應該怎麼去給player減血呢?你看,在MinusBlood()這個方法的整個作用域的範圍之內,並沒有拿到是誰在功擊我這樣一個對象。

如果說,你能傳進來一個參數就好了,比如傳進一個player這個參數,我就知道是這個傢伙,他攻擊了我這麼多的點數,然後我就可以直接去攻擊這個player了,對吧,可惜,它並沒有這個東西。

所以說呢,咱們的想想辦法了,如何能夠在這樣一個多播的調用當中,來告訴那些被調用的方法,到底是誰在攻擊他們。

##

委託類型 再加上 object o 參數

加上 object o 參數,也就是說,咱們這個 OnAttack()方法在調用的時候,第一個參數需要傳一個object類型的對象/物件進去。

object是一切class的父類型

1
2
//這個委託的方法必須是void,需要傳入兩個參數
public delegate void OnAttackDelegate(object o, int attack);

這樣,我是不是可以把我當前這個誰調用 AOE的玩家對象給他傳進去呀。

那誰調用這個 AOE呀,是不是this,我們是不是可以用this這個關鍵字,當player調用內部的AOE(),用this就可以表示當前這個player對象了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//使用委託解耦合
//在 Player 當中聲明委託類型,將需要調用的減血方法,在 Player 類別外設給內部的委託
class Player 
{
    //聲明委託,用來規定減血的方法 應該符合怎麼樣的 方法簽名
    public delegate void OnAttackDelegate(object o, int attack); //加上object參數
    //聲明OnAttackDelegate類型的委託
    public OnAttackDelegate OnAttack = null;

    public void DoAOE() {
        if (OnAttack != null) {
            OnAttack(this,10); //this表示當前這個player玩家
        }
    }
}

OnAttack(this,10);

  • 第一個參數是object類型,我們要傳進去的,其實是當前的這個玩家對象/物件,可以用this
  • 第二個參數attack攻擊數值

這時候,還得再思考一個問題,當我定義委託方法加上一個object參數後,下面加入委託中的所有方法,都需要加上object參數。

1
2
3
4
//Enemy
public void MinusBlood(object o, int attack) { ... }
//NPC
public void BeAttacked(object o, int attack) { ... }

再思考一下

再思考一下,就是說,咱們的玩家在做AOE群攻的時候,可能不光只是讓Enemy們去掉血,有可能還會帶毒,「帶毒」什麼意思,就是我釋放了一個AOE技能,你被我打到了,打到了之後,首先你會扣他十滴血,然後呢,我就跑了,在跑的過程當中,你們這些被我打過的這些敵人們,每隔一秒鐘都可能會被我毒傷,掉一滴血,這樣子。

所以說,我們在DoAOE技能,咱們需要給化再傳一個參數進去,傳什麼參數呢?傳是否中毒。

好,我們這事啊從 Enemy開始說起:先加上一個參數poisoned

1
public void MinusBlood(object o, int attack, bool poisoned) { ... }

想一想,這樣是相當於改變了這個方法的參數列表,我這裡的參數列表一變,你想想,下面是不是直接又要報錯了呀。

為什麼呀,因為我這委託只提供兩個參數:objectattack,對吧,那怎麼辦咧?再修改一次…

因為咱們加了一個效果,或者說策劃他想了一個歪歪點子,這個AOE其實是可以讓這個周圍的敵人中毒的,你看,能不能怎麼改一下呀,改好後呢,又一個需求呃,是否進入「眩暈狀態」,那是不是又要再加一個參數,定義委託、委託方法和所有的被加人委託的方法,是不是又要再加一個參數。

因為 delegate加了一個參數,所有對應的方法,又得加參數,對吧,所以,我們有沒有什麼招,能夠去解決這個問題呢?或者讓這個問題變得簡單一點呢?

問題:委託方法的參數太多,且會經常變動

  • 問題:委託方法,需要攜帶的參數太多了,參數還會經常變動
  • 方法:將所有的參數都包含到一個class裡面

將所有的參數都給他封裝到一個叫做EventArgs類裡面

EventArgs、Player class

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
class EventArgs //事件參數們
{
    public int attack = 10; //攻擊減血量
    public bool poisoned = false; //中毒
}

//使用委託解耦合
//在 Player 當中聲明委託類型,將需要調用的減血方法,在 Player 類別外設給內部的委託
class Player
{
    //聲明委託,用來規定減血的方法 應該符合怎麼樣的 方法簽名
    public delegate void OnAttackDelegate(object o, EventArgs args);
    //聲明OnAttackDelegate類型的委託
    public OnAttackDelegate OnAttack = null;

    public void DoAOE()
    {
        if (OnAttack != null)
        {
            EventArgs args = new EventArgs();
            args.attack = 10;
            args.poisoned = true;
            OnAttack(this, args);
        }
    }

    public void Shout()
    {
        Console.WriteLine("玩家很痛!");
    }
}

Enemy class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//敵人
class Enemy
{
    private int blood = 100;
    public void MinusBlood(object o, EventArgs args)
    {
        Console.WriteLine("好疼,我是Enemy");
        blood -= args.attack; //減血

        //判斷是否中毒
        if (args.poisoned)
        {
            Console.WriteLine("Enemy中毒了");
        }

        //我發動被動功能 背刺
        Player player = (Player)o;
        player.Shout();
    }
}

NPC class

1
2
3
4
5
6
7
8
9
10
//非玩家角色
class NPC
{
    private int blood = 100;
    public void BeAttacked(object o, EventArgs args)
    {
        Console.WriteLine("好疼,我是NPC");
        blood -= args.attack;
    }
}

完整程式碼

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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
namespace SeniorEvent
{
    //需求:
    // 1 Player Class: 能夠釋放AOE範圍攻擊技能,讓波及到的敵人統一減血10滴
    // 2 Enemy Class: 能夠對外提供減血的方法函數,供外界調用

    //問題:委託方法,需要攜帶的參數太多了,參數還會經常變動
    //方法:將所有的參數都包含到一個class裡面

    class EventArgs //事件參數們
    {
        public int attack = 10; //攻擊減血量
        public bool poisoned = false; //中毒
        public bool headache = false; //眩暈
    }

    //敵人
    class Enemy
    {
        private int blood = 100;
        public void MinusBlood(object o, EventArgs args)
        {
            Console.WriteLine("好疼,我是Enemy");
            blood -= args.attack; //減血

            //判斷是否中毒
            if (args.poisoned) {
                Console.WriteLine("Enemy中毒了");
            }

            //判斷是否眩暈
            if (args.headache) {
                Console.WriteLine("Enemy眩暈了");
            }

            //我發動被動功能 背刺
            Player player = (Player)o;
            player.Shout();
        }
    }

    //非玩家角色
    class NPC
    {
        private int blood = 100;
        public void BeAttacked(object o, EventArgs args)
        {
            Console.WriteLine("好疼,我是NPC");
            blood -= args.attack;
        }
    }

    //使用委託解耦合
    //在 Player 當中聲明委託類型,將需要調用的減血方法,在 Player 類別外設給內部的委託
    class Player
    {
        //聲明委託,用來規定減血的方法 應該符合怎麼樣的 方法簽名
        public delegate void OnAttackDelegate(object o, EventArgs args);
        //聲明OnAttackDelegate類型的委託
        public OnAttackDelegate OnAttack = null;

        public void DoAOE()
        {
            if (OnAttack != null)
            {
                EventArgs args = new EventArgs();
                args.attack = 10;
                args.poisoned = true;
                args.headache = true;

                OnAttack(this, args);
            }
        }

        public void Shout()
        {
            Console.WriteLine("玩家很痛!");
        }
    }

    internal class Program
    {
        static void Main(string[] args)
        {
            Player player = new Player();

            Enemy e0 = new Enemy();
            Enemy e1 = new Enemy();
            Enemy e2 = new Enemy();

            NPC npc = new NPC();

            player.OnAttack += e0.MinusBlood;
            player.OnAttack += e1.MinusBlood;
            player.OnAttack += e2.MinusBlood;
            player.OnAttack += npc.BeAttacked;

            player.DoAOE();

            Console.Read();
        }
    }
}

AOE技能如何能夠解耦合呢?

  • Player做了一次折分:
    • 在裡面做了一個 delegate委託OnAttackDelegate
    • 然後做了一個委託對象/物件叫做 OnAttack
    • 然後將一大堆方法都加到了OnAttack裡面了
  • 隨後提了需求:
    • Enmey希望知道誰攻擊他
    • Enmey希望知道一些額外數據(是否中毒/眩暈等)
  • 所以對整個架構做了一個改變:
    • 首先呢,咱們定義了一個叫做做 EventArgsclass
    • 在這裡面,咱們將這個受害者,他所想知道的一切信息,都給他封裝到了這個EventArgs裡面
  • 其次吧,咱們將Player 施暴者,他裡面的這個 OnAttackDelegate做了改變:
    • 讓他能夠向外傳導一個object, 而且向外傳導一個EventArgs對象。
    • 在這裡面,object其實就是他自己,將這個施暴者或者攻擊者自己給放進去OnAttack(this, args)
    • 然後args的話,在這裡new 了一個EventArgs,將本次攻擊相關的一些參數,都填寫到了args裡面,把args傳出去。
  • 隨後咱們在這個接收端Enemy裡面:
    • 咱們MinusBlood()是接收一個object、接受一個EventArgs
    • 然後根據咱們接收到的這個攻擊者是誰,攻擊參數是怎樣的,可以在這裡面做各式各樣的邏輯判斷

EventHandler 真正的偷懶(工程師進步的階梯)

1
2
3
4
class Player {
    public delegate void OnAttackDelegate(object o, EventArgs e);
    public OnAttackDelegate OnAttack = null;
}

我們不是定義一個委託叫做OnAttackDelegate,他裡面傳了一個 object,這個object就是,誰發出了這個事件,比如說,Player發出了攻擊,那他就是事件的一個發出者,然後發出者是不是還攜帶了一個叫EvnetArgs的這樣一個參數包,然後,我們再用 OnAttackDelegate 定義了一個OnAttack這樣的委託對象/物件,對吧。

事件專用委託定義 EventHandler

C# 為我們寫好了事件專用委託定義

其實C#已經為我們在全局層面上定義好了這樣的委託類型

1
2
3
//sender: 誰發出這個事件
//EventArgs: 微軟為我們寫了的參數包
public delegate void EventHander(object? sender, EventArgs e);

既然微軟都幫我們寫好了EventArgs參數包,我們直接拿來用不就完事了呀,我還需要再去聲明一個 xxx delegate 類型嗎,不需要了。

我直接用微軟定義好的EventHandler這樣的委託類型,直接聲明一個委託對象/物件Onattack不就完事了嗎

1
2
3
class Player {
    public EventHandler Onattack = null;
}

進一步思考事件:

事件對應的委託,「不應該」被類別外界調用,只能由某個操作觸發

OnAttack()裡有很多的方法,必須使用技能才會觸發的。如果哪天某個程式員在外界調了一下OnAttack(),周圍的怪物都減血了,那這就奇怪了,玩家跑一跑,這周圍的這個怪物都減血了,太神奇了,是不是開外掛了。

那真正意義上應該怎麼做呢?

我們應該在外界調用 DoAOE(),由DoAOE()內部去調用OnAttack()才對。

  • 第一個思考點:類內部應該有個事件,而這個事件他所對應的委託,不應該被類外部調用的,只能由某個操作觸發

事件對應的委託,「不應該」被類外界直接賦值,只能夠通過 +、-增減委託方法

1
2
Player player = new Player();
player.OnAttack += 某方法

事件委託 event

  • 為了能夠滿足兩個條件:
    • 事件對應的委託,「不應該」被類外界調用,只能由某個操作觸發
    • 事件對應的委託,「不應該」被類外界直接賦值,只能夠通過+-增減委託方法
1
2
3
class Player {
    public event EventHandler OnAttack = null;
}

event關鍵字,讓委託升級了,變成了「事件委託」。

  • Event事件規則
    • event關鍵字修飾的委託,只能夠定義在某個類內
    • event關鍵字修飾的委託,只能夠被當前類別方法觸發執行,類外不可觸發執行
    • event關鍵字修飾的委託,只能通過+-增減委託方法,不可賦值

程式碼練習 event, EventHandler

  • 事件(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
42
43
44
45
46
47
48
49
/*
 * Event(事件)
 *  1 event修飾的委託,只能在類內調用執行,類外不可調用的
 *  2 event修飾的委託,不能直接賦值,只能通過+、-增減其中蘊含的方法
 */
namespace EventTest
{
    class Player {
        //定義Player內部會被觸發的事件委託
        public event EventHandler OnAttack = null;

        public void DoAOE() {
            if (OnAttack != null) {
                OnAttack(this, EventArgs.Empty);
            } 
        }
    }

    class Enemy {
        //定義可以被加到OnAttack委託當中的方法
        public void AttackMe(object sender, EventArgs e) {
            Console.WriteLine("我被攻擊了!");
        }

    }
    internal class Program
    {
        static void Main(string[] args)
        {
            Player player = new Player();
            Enemy enemy = new Enemy();

            //沒有加上event關鍵字,會有兩種錯誤寫法

            //1.在類外直接調用event修飾的委託,是禁止的
            //player.OnAttack(new object(), EventArgs.Empty);//錯誤寫法1

            //2.直接賦值是禁止的
            //EventHandler handler = new EventHandler(enemy.AttackMe);
            //player.OnAttack = handler;//錯誤寫法: 直接賦值
            //handler(new object(), EventArgs.Empty); //錯誤寫法: 類外直接調用

            player.OnAttack += enemy.AttackMe;
            player.DoAOE();

            Console.Read();
        }
    }
}

總結

事件中的角色

事件運行流程,需要先定義一個事件,然後呢,每一個需要去訂閱該事件的對象,你也得有一些處理的方式,當事件觸發之後,會連加連拖帶口的給你全部都送過來了,把這些相關的參數、時間對象,就扔到咱們的每一個訂閱者的處理方法裡面,你們各自去處理吧,愛怎麼做就怎麼做,我也不管你,我就告訴你,我觸發了這個事件了。

咱們有「玩家」、「敵人」兩個class,他們兩個也會被new成兩個對象/物件:

  • 玩家:稱為「事件源對象」Event Source。因為是玩家發了AOE技能才迫使OnAttack這種事件發生的,所以說,人家他是「事件源」。
  • 敵人:叫做「響應對象」或者是「訂閱者對象」Event Subscriber,就好像你訂報紙一樣,交上報錢報紙來了,就給你送過來了,沒有報紙沒不會送過來了。
  • 事件(event)-OnAttack:然後玩家有一個OnAttack,這個OnAttack是被event所修飾的,所以他就叫做「事件(event)」。
  • 事件處理方法(EventHandler):然後敵人有一個AttackMe()方法,它是被加到了OnAttack這個委託裡面,所以AttackMe()又稱為叫做「事件處理的方法」。
  • 也就是說,當OnAttack這個事件觸發的時候,他會調用每一個敵人的AttackMe()這樣的方法去處理這個事件,所以他叫做「事件處理方法」。
  • +=訂閱該事件(Subscribe): 我們用這樣的「事件處理方法」去訂閱了OnAttack這樣的事件,其實呢,通過的是一個+=這樣的符號,將AttackMe()加到了OnAttack這個委託裡面。
  • 所以,換句話說,咱們的敵人呀,通過AttackMe()這樣的事件處理方式,訂閱了OnAttack這樣的事件。
  • 事件觸發:接下來,當OnAttack被觸發的時候,什麼叫做「被觸發」呀?就是玩家DoAOE()的時候呀,發使用AOE技能 DoAOE()啦,然後就會直接觸發OnAttack這樣的事件。
  • 這個事件觸發之後,就會調用AttackMe()這樣的事件處理方法。
  • 然後在事件觸發的過程當中,需要傳導參數,我們傳導了兩個參數:「事件源對象object o」、「事件參數EventArgs e」給到了AttackMe()這樣的方法。
    • 事件源對象object o:在AttackMe()方法裡面,他得知道誰攻擊了我,所以就是object o
    • 事件參數EventArgs e:然後通過EventArgs事件包,傳一些參數過去。

https://www.bilibili.com/video/BV1ou411a7YD/

This post is licensed under CC BY 4.0 by the author.

[C# 筆記] 多播委託 Multicast-Delegate

[C# 筆記] 事件參數泛型與練習