本節內容參考《你必須知道的.NET》和《C#進階編程(第四版)》
一、瞭解委託
我們知道回呼函數實際上就是方法調用的指標,也就是通常所說的函數指標。那麼在.NET中,以委託的方式來實現了函數指標的概念。.NET中使用委託的主要原因是它是型別安全的,為什麼呢?因為在以前,比如C中,函數指標只不過是一個指向儲存單元的指標,我們無法說出這個指標實際指向什麼,而委託確定了指向方法的傳回值類型和參數列表。注意的是:委託並不等同於方法,而是一個參考型別。
一、委託的定義
委託的定義
//第1步:聲明一個委託
public delegate void CalculateDelegate(int x, int y);
//第2步:建立與委託關聯的方法,二者具有相同的傳回值類型和參數列表
public void Add(int i, int j)
{
MessageBox.Show((i+j).ToString());
}
//第3步:定義委託類型變數
private CalculateDelegate myDelegate;
public void GetDelegateEx()
{
//第4步:進行委託綁定
myDelegate = new CalculateDelegate(Add);
//第5步:回調Add方法
myDelegate(1, 2);
}
三、多播委託
在多播委託中需要注意兩點:
(1)+=和-=操作,其實它們分別調用了Delegate.Combine和Delegate.Remove方法
(2)多播委託的傳回值一般為Void,如果委託類型為非Void類型,那麼多播委託將返回最後一個調用的方法的執行結果,實際中不推薦這樣應用。
四、委託的本質
前面我們提到過委託是一個參考型別,其本質上它是一個類,把上邊的代碼編譯為IL:
從中我們可以看出:
(1)委託CalculateDelegate是一個類,它繼承自System.MulticastDelegate
(2)CalculateDelegate的建構函式:在建立一個委託類型執行個體時,將會為其初始化一個指向對象的引用(這裡指向DelegateEx對象)和一個標識回調方法的整數,由編譯器完成。
(3)真正執行調用的是Invoke方法。
五、委託和事件
從前面的範例程式碼中可以看出,在用戶端我們可以隨意對委託進行操作,這在一定程度上破壞了物件導向的封裝機制。.NET的事件模型建立在委託機制之上,它實現了對委託的封裝。
事件發送器:可以是應用程式中的一個對象或程式集等,主要作用是引發事件。
事件接收器:發生某些事情時被通知的任何應用程式、對象或組件。
發送器怎麼通知接收器呢?我們在事件接收器的某個地方定義一個方法,它負責處理事件, 在每次發生登入的事件時,就執行這個事件處理常式。由於發送器對接收器一無所知,這時就要使用委託作為中介。發送器定義接收器要使用的委託,接收器將事件處理常式註冊到事件中。
先瞭解一下這段代碼:btnSave.Click += new EventHandler(btnSave_Click)。我們在程式設計中經常見到,它告訴我們:在引發btnSave按鈕的Click事件時,應執行btnSave_Click方法。EventHandler是事件用於把處理常式(btnSave_Click)賦予事件(Click )的委託。
Code
//定義一個內部事件參數類型,用於存放當事件引發時向處理常式傳遞的狀態資訊。
public class CalculateEventArgs : EventArgs
{
public readonly int x, y;
public CalculateEventArgs(int x,int y)
{
this.x = x;
this.y = y;
}
}
//聲明事件委託。
public delegate void CalculateEventHandler(object sender, CalculateEventArgs e);
public class Calculator
{
//定義事件成員,提供非正規繫結。
public event CalculateEventHandler MyCalculate;
//定義負責通知事件引發的方法,也就是委託的Invoke方法調用。
protected virtual void OnCalculate(CalculateEventArgs e)
{
if (MyCalculate != null)
{
MyCalculate(this, e);
}
}
//調用該方法就表示有新的事件方法。
public void Calculate(int x, int y)
{
CalculateEventArgs e = new CalculateEventArgs(x, y);
//通知所有的事件的註冊者
OnCalculate(e);
}
}
上面定義了一個完整的事件程式,現在只需定義個事件觸發程式。
public class CalculaterManager
{
public void Add(object sender, CalculateEventArgs e)
{
MessageBox.Show((e.x + e.y).ToString());
}
}
static void Main()
{
Calculator calculator = new Calculator();
CalculaterManager manager = new CalculaterManager();
calculator.MyCalculate += manager.Add;
calculator.Calculate(1, 2);
}