天天看點

委托與事件

委托

 定義委托的文法和定義方法比較相似,隻是比方法多了一個關鍵字delegate ,我們都知道方法就是将類型參數化,所謂的類型參數化就是說該方法接受一個參數,而該參數是某種類型的參數,比如int、string等等;而委托是将方 法參數化,說了上面的那個類型參數化之後,相信你也能猜到方法參數化的意思了,對,就是将方法作為一個參數傳到一個委托中。

首先來看看聲明委托的語句:

public deletate void MyDelegate();  

public:通路修飾符  delegate:關鍵字  void:傳回類型  MyDelegate:委托名稱  ( ):參數清單  

看到聲明大家會想了,為什麼該委托的傳回值,參數清單要這樣的,我不能傳回一個 string,一個int麼?我不能給委托加幾個參數麼? 答案是:當然可以,但委托的定義是相對于方法來說的,因為得你的委托最終是要來注冊方法的,而你的方法是具有某種簽名的,是以你要給怎樣簽名的方法來聲明 一個委托,該委托就要和該方法具有同等的簽名,就類似于你用一個int 類型的變量去接受一個string類型的值,顯然是不行的(個人了解).... 

 * 委托隻要定義就可以了,我們并不需要關心他的實作  

委托的使用

注冊委托有兩種方法:

第一種:直接将方法指派[=]或者用“+=” 給一個委托==>委托名 =[+=]  方法名 

第二種:委托本質也是一個類,隻是一個特殊的類,是以我們也可以執行個體化一個委托對象通過委托構造函數來注冊委托==》委托名 對象名= new 委托名(方法名) 

 了解了委托的聲明和使用,我們就可以來看小例子來加深了解了

 首先看看界面: 

委托與事件

界面上就是簡單的四個按鈕 兩個屬于委托,兩個屬于事件,都是一個用來執行,一個用來幹擾,以便于來了解委托事件

然後看背景代碼,首先我定義了一個Test類,聲明委托,執行個體了委托,還聲明了事件,寫了個方法用來觸發事件,代碼如下: 

委托與事件
委托與事件

 1     public class Test

 2     {

 3         //聲明一個委托

 4         public delegate void MyDelegate();

 5 

 6         //建立一個委托執行個體

 7         public MyDelegate myDel;

 8         //聲明一個事件

 9         public event MyDelegate EventMyDel;

10 

11         //事件觸發機制(必須和事件在同一個類中) 外界無法直接用EventMyDel()來觸發事件

12         public void DoEventMyDel()

13         {

14             EventMyDel();            

15         }    

16     }

委托與事件

然後為了友善測試,我還定義了三個方法在Form1類,代碼如下:

委托與事件
委托與事件

 1         //方法A

 2         public void Fun_A()

 3         {

 4             MessageBox.Show("A 方法觸發了");

 5         }

 6 

 7         //方法B

 8         public void Fun_B()

 9         {

10             MessageBox.Show("B 方法觸發了");

11         }

12         //方法C

13         public void Fun_C()

14         {

15             MessageBox.Show("C 方法觸發了");

16         }

委托與事件

然後在頁面Page_Load時注冊委托,将方法A和方法B挂載到委托鍊上,代碼如下:

委托與事件
委托與事件

 1        //頁面載入事件

 2         private void Form1_Load(object sender, EventArgs e)

 4             

 5            //注冊委托(挂載方法)

 6            test.myDel += Fun_A;

 7            test.myDel += Fun_B;

 8 

 9            //注冊事件(挂載方法)

10            //test.EventMyDel += Fun_A;

11            //test.EventMyDel += Fun_B;

12         }

委托與事件

然後給[執行委托]按鈕添加Click事件,代碼如下:

委托與事件
委托與事件

1        //執行委托

2         private void button1_Click(object sender, EventArgs e)

3         {

4             //執行委托鍊中的方法

5             test.myDel();

6         }

委托與事件

顯示效果:

委托與事件
委托與事件

說明沒問題,方法都挂載到委托鍊上了,但是如果這時候我們來進行下幹擾呢?各位想想看,結果會是怎麼樣了呢,先貼上幹擾的代碼:

委托與事件
委托與事件

1       //委托幹擾事件

2         private void button2_Click(object sender, EventArgs e)

4             //給委托賦 null 值

5             test.myDel = null;

7             //給委托挂載上C方法

8             test.myDel += Fun_C;

9       

委托與事件

其實這裡的所謂的“幹擾”,就是給以存在的委托鍊中附加了一個 null 的對象,然後在給委托附加C方法,此時按如下步驟運作程式:

委托與事件

看看結果會怎麼樣,哎,發現就隻有C方法被觸發了:

委托與事件

大家想想,為什麼會這樣呢??為什麼A,B方法不執行了呢??這是因為原來的委托鍊 是已經挂載了A,B兩個方法了,但這時突然被幹擾了下,又附加了一個null對象,破壞了原有的委托鍊了,但這又說明了什麼呢?因為委托本質就是一個類, 它包含一對有用的字段,第一個字段是存放該對象的引用,第二個字段存放一個方法的指針,是以我們可以把委托了解成一個指向函數的指針,當一個方法作為參數 指派給一個委托的時候,該委托就指向了該方法的首位址,即方法名,是以當我們給委托注冊A,B兩個方法,該委托就同時指向了A,B兩個方法的首位址,但這 是又突然給委托指派了,且指派了一個null對象,注意這裡用的是指派符号[=],這就是說讓該委托清除原有的指針指向,此時指向一個null,之後又給 委托注冊了C方法,是以此時委托即指向null,又指向了C方法的首位址,這就是為什麼運作時隻會看到C方法被觸發的原因了!

那就是說現在的委托變得不安全了,哪天一個項目中給委托注冊了很多方法,但突然被幹擾了下,前面的注冊都失效了,那我們前面做的工作不是白做了,那有沒有辦法可以防止這種幹擾呢??答案是當然有,相信聰明的你也應該猜到了,這時就是事件該上場的時候了。

事件

事件就是一個特殊的委托,委托和事件就類似于字段和屬性的關系,事件是對委托做了一個封裝(這是個人了解)

先看看聲明一個事件:

public Event MyDelegate EventMyDel;

public:通路修飾符  Event:關鍵字 MyDelegate:委托 EventMyDel:事件名稱

接下來讓我們來看看我們用反編譯工具來反編譯下該項目的exe可執行檔案,看看當我們給事件注冊上方法時,事件内部在做什麼,附上反編譯代碼:

委托與事件

從 反編譯中就可以充分看出,事件的本質就是一個委托,它的内部仍然是用一個委托,該委托的名稱的方法就隻有和事件名首字母的大小寫不同而已,該委托來負責接 收事件,然後通過add,remove方法來實作委托的注冊和删除,即當我們給委托注冊一個方法時,内部就調用add方法,通過 Delegate.Combine()方法來實作将方法附加到委托鍊上,當我們删除委托鍊上的一個方法時,即内部調用remove方法,通過 Delegate.Remove()方法,将該方法從委托鍊上移除,是以通過反編譯工具,我們就可以清楚的知道事件内部的實作代碼了。

接下來看看事件的效果,首先在Page_Load事件中注冊事件,我們将A,B方法注冊到事件上,代碼如下:

委托與事件
委托與事件

 5            ////注冊委托(挂載事件)

 6            //test.myDel += Fun_A;

 7            //test.myDel += Fun_B;

 9            //注冊事件

10             test.EventMyDel += Fun_A;

11             test.EventMyDel += Fun_B;

委托與事件

然後我們點選[執行事件]按鈕.貼上代碼:

委托與事件
委托與事件

1        //執行事件

2         private void button3_Click(object sender, EventArgs e)

4             //執行事件觸發方法

5             test.DoEventMyDel();

委托與事件

看看效果,如圖:

委托與事件
委托與事件

和剛開始的委托一樣,沒有問題,兩個方法都觸發了,接下來再讓我們來實施幹擾,附上幹擾代碼:

委托與事件

1        //幹擾事件

2         private void button4_Click(object sender, EventArgs e)

4             //注冊幹擾事件

5             test.EventMyDel = null;

6        

慢着,,此時編譯報錯了,報了什麼錯呢,,我們看看:

委托與事件

說明了什麼,,說明我們沒法對事件用指派[=]号來進行注冊,這就避免了破壞直接委托鍊的指針指向了,,但你會想了,那如果我們用附加[+=]上一個null對象呢?會幹擾到麼??好的,,我們實施下,修改代碼,如下:

委托與事件
委托與事件

 1         //幹擾事件

 2         private void button4_Click(object sender, EventArgs e)

 4             //注冊幹擾事件

 5             test.EventMyDel += null;

 7             //注冊C方法

 8             test.EventMyDel += Fun_C;

 9 

10         }

委托與事件

運作如圖:

委托與事件

運作結果如下:

委托與事件
委托與事件
委托與事件