C#委托與事件初探


最近剛剛接觸C#,學到事件與委托部分無法理解,於是上網查閱了各種資料,終於明白了一些,在此進行總結。

 

一.C語言中的函數指針

  想要理解什么是委托,就要先理解函數指針的概念。所謂函數指針,就是指向函數的指針(等於沒說-.-)。比如我定義了兩個函數square和cube分別用於計算一個數的平方和立方,我再定義函數指針calcu,然后我讓calcu指向square,那么調用calcu時就相當於調用了square函數(注意,此處函數指針接受的參數類型及個數要與函數一致)。很好理解吧?不多說,上代碼。

 1 #include <stdio.h>
 2 
 3 void square(int x) { printf("square of %d is %d\n",x,x*x); }
 4 void cube(int x) { printf("cube of %d is %d\n",x,x*x*x); }
 5 
 6 int main()
 7 {
 8     void (*calcu)(int x);
 9     calcu=square;
10     calcu(2);
11 
12     return 0;
13 }

 

二.C#中委托的實質

  委托又名委托類型,為什么C#弄出這個東西?因為C#是一門比較安全的語言,不允許操作指針,於是我們不能定義函數指針。但想要達到相同的效果,於是定義了委托類型。所謂委托類型,其本質就是C中的指針類型。於是代碼變成了這樣:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Delegate
 8 {
 9     class Program
10     {
11         static void square(int x) { Console.WriteLine("square of {0} is {1}", x, x * x); }
12         static void cube(int x) { Console.WriteLine("cube of {0} is {1}", x, x * x * x); }
13 
14         delegate void math(int x); //定義委托類型
15 
16         static void Main(string[] args)
17         {
18             math calcu;
19             calcu += square;
20             calcu(2);
21             Console.ReadKey();
22         }
23     }
24 }

  可以看出,定義委托類型math實際上就相當於定義了void*類型。而委托類型實例化得到的calcu實際上就是函數指針。(說句題外話:定義函數(方法)時要加上static是因為調用函數時並未實例化,只有靜態方法能夠直接通過類調用)。

 

三.委托的使用方法

  我們在上述代碼19行后面加上一行代碼 calcu+=cube; 運行會發現,square和cube均被調用。可以看出,符號 += 表示綁定方法到委托變量,同理符號 -= 表示取消綁定。可以理解為calcu是void **類型,即它指向了一個數組,數組中的每一項都是函數指針類型,每次調用calcu時,遍歷此數組,即依次調用每個綁定的方法。

 

四.封裝與事件的引入

  下面我們要用面向對象的思想將上述代碼進行封裝,使其變清晰。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Delegate
 8 {
 9     public delegate void math(int x);
10     public class Calcu
11     {
12         public math calcu;
13     }
14 
15     class Program
16     {
17         static void square(int x) { Console.WriteLine("square of {0} is {1}", x, x * x); }
18         static void cube(int x) { Console.WriteLine("cube of {0} is {1}", x, x * x * x); }
19         
20         static void Main(string[] args)
21         {
22             Calcu c = new Calcu();
23             c.calcu += square;
24             c.calcu += cube;
25             c.calcu(2);
26             Console.ReadKey();
27         }
28     }
29 }

      由於委托變量是public的,封裝的程度很低,在外部可以任意修改。為了改進這個問題,C#引入了事件。

  所謂事件,實際上還是委托的實例化,只是其內部多了一些定義,多了一些限制。其一,事件實際上聲明了一個private類型的委托變量,因此在類外無法直接調用。

  於是我們將上述代碼的第12行改成這樣:

public event math calcu;

  這句代碼會被編譯器解釋成private math calcu和一些方法。

  運行之后25行報錯了,因為calcu是private的,不能直接調用。但23,24行並沒有報錯。那么問題來了,為什么我們可以用+=來給calcu綁定方法呢?

  因為其二,事件還幫我們干了一件事情,就是定義了綁定方法和取消綁定方法的函數,它們是public的,並且將運算符+=,-=重載,和這兩個函數對應。

  好了,現在我們要寫一個接口函數來完成計算:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Delegate
 8 {
 9     public delegate void math(int x);
10     public class Calcu
11     {
12         public event math calcu;
13         public void calculate(int x)
14         {
15             calcu(x);
16         }
17     }
18 
19     class Program
20     {
21         static void square(int x) { Console.WriteLine("square of {0} is {1}", x, x * x); }
22         static void cube(int x) { Console.WriteLine("cube of {0} is {1}", x, x * x * x); }
23         
24         static void Main(string[] args)
25         {
26             Calcu c = new Calcu();
27             c.calcu += square;
28             c.calcu += cube;
29             c.calculate(2);
30             Console.ReadKey();
31         }
32     }
33 }

  至此,基本概念已經清晰。

  想來,使用事件會讓人不得不將對象封裝起來,這應該就是面向對象思想的體現吧。

 

參考資料:http://www.tracefact.net/CSharp-Programming/Delegates-and-Events-in-CSharp.aspx


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM