程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#--- 之"委托、事件" 面面觀

C#--- 之"委托、事件" 面面觀

編輯:C#入門知識

委托與事件,它們的應用非常廣泛,為了便於復習,我特地將它們總結了一下。
 
一、委托
委托,通俗的講,就是‘方法’的容器。
是用來存放和調用方法用的。
 
下面這個例子,簡單的介紹一下委托的用法:
 
[csharp] 
public delegate void  SayHi_Delegate(string name); 

這就是一個委托,任何形如 void **(string **);的函數,都可以使用這個委托來調用。
比如:
[csharp]
private static void SayHiEn(string name)   //說英文 
    { 
        Console.WriteLine("Hi  {0}, I am ZeroCool_24!",name);  
    } 
 
    private static void SayHiCh(string name)   //說中文 
    { 
        Console.WriteLine("你好  {0}, 我是 ZeroCool_24!", name);  
    } 

 
調用的方法如
[csharp]
static void Main(string[] args) 

    SayHi_Delegate Hi; 
    Hi = SayHiCh; 
    Hi += SayHiEn; 
 
    Hi("cyh"); 
 
    Console.ReadLine();  

輸出結果為:


可以看到,在向SayHi方法容器中添加方法的時候,使用了 += ,同理還可以用 -=; 值得注意的一點是,容器內必須至少要有一個方法。
 
我們知道,可以將方法作為參數來傳遞,同理的,我們也可以將委托(方法的容器),作為參數來進行傳遞,如下:
首先定義接收委托的函數
[csharp] 
private static void GreetPeople(string name, SayHi_Delegate MakeGreeting) 
        { 
            MakeGreeting(name); 
        } 
 
接下來調用一下:
 
[csharp]
static void Main(string[] args) 
  { 
      SayHi_Delegate Hi; 
      Hi = SayHiCh; 
      Hi += SayHiEn; 
 
      //Hi("cyh");     
      GreetPeople("cyh", Hi);  
 
      Console.ReadLine();  
  } 

得到的結果與上面是一樣的。
二、事件
由於大家已經對委托有了一個初步的認識,下面,我們將這個例子做一個改進
[csharp] 
using System; 
 
namespace 委托 
{  
    public delegate void SayHi_Delegate(string name);   
  
    public class GreetClass //新的類 
    { 
        public void GreetPeople(string name, SayHi_Delegate MakeGreeting) 
        { 
            MakeGreeting(name); 
        } 
    } 

 
[csharp]
using System; 
 
namespace 委托 

    class TestClass 
    { 
        private static void SayHiEn(string name) 
        { 
            Console.WriteLine("Hi  {0}, I am ZeroCool_24!",name);  
        } 
 
        private static void SayHiCh(string name) 
        { 
            Console.WriteLine("你好  {0}, 我是 ZeroCool_24!", name);  
        } 
 
        static void Main(string[] args) 
        { 
            SayHi_Delegate Hi; 
            Hi = SayHiCh; 
            Hi += SayHiEn; 
 
            //Hi("cyh");     
            GreetClass greet = new GreetClass();     
            greet.GreetPeople("cyh", Hi); 
 
            greet.GreetPeople("zc", SayHiCh);   //SayHiCh()非 SayHi_Delegate 委托,但是因為可以轉化成一個只含SayHiCh()方法的委托。 
 
            Console.ReadLine();  
        } 
    } 


結果如下:

 
演示的結果很好,看上去好像也沒有問題。但是,能不能更好的處理呢?
答案是肯定的。應用面向對象的思想,我們應該將main()中的委托變量Hi封裝到GreetClass類中。
[csharp] 
using System; 
 
namespace 委托 
{  
    public delegate void SayHi_Delegate(string name);   
  
    public class GreetClass 
    { 
        public  SayHi_Delegate Hi;      //SayHi_Delegate的實例 
 
        public void GreetPeople(string name, SayHi_Delegate MakeGreeting) 
        { 
            MakeGreeting(name); 
        } 
    } 

[csharp] 
static void Main(string[] args) 
        { 
             GreetClass greet = new GreetClass(); 
            greet.Hi = SayHiCh; 
            greet.Hi += SayHiEn; 
 
            greet.GreetPeople("Cyh_Zc",greet.Hi);     
 
            Console.ReadLine();  
        } 

 


這樣,又有問題出來了,委托變量Hi雖然被封裝到了GreetClass,但是,客戶端依然可以隨意的訪問它,這就帶來了一個安全性的問題,如果現在的Hi不是SayHi_Delegate類型,而是string類型,我們可以使用屬性來解決。
但是,委托變量該如何解決呢?
沒錯,就是事件。Event,它封裝了委托類型的變量,使得:在類的內部,不管你聲明它是public還是protected,它總是private的。在類的外部,注冊“+=”和注銷“-=”的訪問限定符與你在聲明事件時使用的訪問符相同。
 
我們來改寫GreetClass類:
 
[csharp] 
public class GreetClass 
    { 
        public event SayHi_Delegate Hi;      //SayHi_Delegate的事件,只是修飾符多了一個event                         public void GreetPeople(string name) 
        { 
            Hi(name); 
        } 
    } 
[csharp] 
static void Main(string[] args) 
        { 
            GreetClass greet = new GreetClass(); 
            //greet.Hi = SayHiCh;   //編譯錯誤                                                 greet.Hi += SayHiCh; 
            greet.Hi += SayHiEn; 
 
            greet.GreetPeople("Cyh_Zc");     
 
            Console.ReadLine();  
        } 

 
可以看到,在使用greet.Hi = SayHiCh的使用,出現了編譯錯誤,這是為什麼呢?
原來 greet.Hi 只能出現在 += 或 -= 的左邊(從類“GreetClass”中使用時除外)。為什麼會這樣?
我們用.NET Reflector來看看GreetClass編譯後的過程:
[csharp] 
public class GreetClass 

    // Fields 
    private SayHi_Delegate Hi; 
 
    // Events 
    public event SayHi_Delegate Hi; 
 
    // Methods 
    public GreetClass(); 
    public void GreetPeople(string name); 

看到上面編譯之後的代碼,大家會疑惑,為什麼會多了一個(Fields)  private SayHi_Delegate Hi;
原來,public event SayHi_Delegate Hi;這行代碼編譯之後,會默認自動的新增一個private的字段SayHi_Delegate Hi;
 
猜想:這個private SayHi_Delegate類型的字段Hi是在GreetClass中使用的,Public 事件 Hi是供外部使用的。
驗證:查看GreetClass中的Hi(name);          
發現引用的是:
[csharp] 
private SayHi_Delegate Hi; 
再查看Main()方法中的greet.Hi,可以看到:
[csharp] 
public event SayHi_Delegate Hi 

    add 
    { 
        SayHi_Delegate delegate3; 
        SayHi_Delegate hi = this.Hi; 
        do 
        { 
            delegate3 = hi; 
            SayHi_Delegate delegate4 = (SayHi_Delegate) Delegate.Combine(delegate3, value); 
            hi = Interlocked.CompareExchange<SayHi_Delegate>(ref this.Hi, delegate4, delegate3); 
        } 
        while (hi != delegate3); 
    } 
    remove 
    { 
        SayHi_Delegate delegate3; 
        SayHi_Delegate hi = this.Hi; 
        do 
        { 
            delegate3 = hi; 
            SayHi_Delegate delegate4 = (SayHi_Delegate) Delegate.Remove(delegate3, value); 
            hi = Interlocked.CompareExchange<SayHi_Delegate>(ref this.Hi, delegate4, delegate3); 
        } 
        while (hi != delegate3); 
    } 

 
說明了上面的猜想是正確的。
看到event Hi()中,有兩個方法:add()和remove(),我想大家應該已經猜到,這兩個函數對應了greet.Hi的兩個操作 += 和 -= 。這下我想大家也應該知道了為什麼greet.Hi = SayHiCh;的時候會報錯了------因為,Hi事件中沒有與 ‘=’對應的方法。
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved