委托與事件,它們的應用非常廣泛,為了便於復習,我特地將它們總結了一下。
一、委托
委托,通俗的講,就是‘方法’的容器。
是用來存放和調用方法用的。
下面這個例子,簡單的介紹一下委托的用法:
[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事件中沒有與 ‘=’對應的方法。