理解C#中的委託(Deligate) 原作者:lakshimi patil 翻譯:tongzhiyong
這個指南敘述了一些C#語言中的主要特性,即委託和事件。
文章中原始碼地址:
http://www.codeproject.com/useritems/Deligates/Deligates.zip
導言
什麼是委託?
我們都熟悉函數。函數執行預先定義的一組操作並且根據傳入的參數來返回結果。例如,函數AddOper(a,b)將兩個數a和b相加然後返回這兩個數的和。現在我們來考慮寫一個通用的函數。這個函數可以執行許多任務。我們假設它能完成數位加,減,乘的操作。
寫這個函數的一種方式可以傳遞我們想要執行操作的類型和值來作為參數。例如:AddOper(a,b,"add")來實現a和b相加。在函數內部,我們使用Select Case或者if else語句來決定我們想要執行操作的類型。這種方法有明顯的複雜度和局限性。
我們想要一種非常靈活而且普通的解決方案。我們想寫一個被主應用程式(Main Application)調用的普通函數來代替調用特定的函數。這正是我們所希望的。
現在的問題是,是否有可能寫出這樣靈活的應用程式呢?
答案是肯定的,我們可以實現將一個方法傳遞給另一個方法。就像GenericFun(AddOper)這樣。然而,出於型別安全的原因,C#不建議這樣的實現方法。為達到同樣的目的,C#提出了一種叫做DELEGATE(委託)的新對象。
與類(Class)類型相似,我們定義委託(Delegate)類型,然後建立一個委託的執行個體來控制函數的細節,請注意這個執行個體只包含函數名和傳回型別的詳細資料,並不包含函數體。
聲明一個委託的文法如下:
Public delegate int GenericFun(int a, int b);
以上定義的這個委託接受傳回型別為整型並且帶有兩個整型參數的函數。
我們想要對以下這個加法函數起作用:
Private int AddOper(int a,int b){}
現在我們建立這個委託的一個執行個體然後將我們的加法函數傳入這個委託。
GenericFun gfAdd = new GenericFun(AddOper);
可以看到委託的建構函式接受函數名作參數。
現在,讓我們來看一下我們如何將這個委託傳遞到一個普通的函數PrintResult中,這個函數可以操縱兩個數字並且返回一個整數。它可以用來列印加,減乘或者任意過程的類型的結果,這些過程帶有兩個整型參數並且返回一個整數。現在,讓我們看一看如何構造PrintResult來處理兩個數相加。
//定義兩個要相加的數字
int No1=40;
int No2=60;
//調用普通函數來將這些數相加
PrintResult(gfAdd,No1,No2)
你將在控制台看到100的結果。
現在我們將看到PrintResult方法是如何定義的。
Private int PrintResult(GenericFun action,int a,int b){
int result=action(a,b);
console.WriteLine(result);
}
你有沒有注意到這樣神奇的效果!只是建立了一個委託的執行個體,傳入一個要協作的函數名然後調用普通函數,並將委託傳入就可以得到你想要的結果。重複相同的過程,實現減法函數,操作如下:
GenericFun gfSub = new GenericFun(SubOper);
PrintResult(gfSub,No1,No2);
我們可以定義一個委託數組,然後定義每一個獨立的委託。我們可以按如下操作:
GenericFun[] gf = {new GenericFun(AddOper),new GenericFun(SubOper)};
然後執行減法操作,使用如下的文法:
PrintResult(gf1,No1,No2);
讓我們看一下如何將委託和事件一起使用。注意事件是代理的一種類型。
為了明白委託是什麼以及如何使用委託,讓我們從一個簡單的例子開始。一個銀行有一個話務中心,它接受ATM卡的資訊。在接受到資訊後,銀行系統通過一下一種方式發送客戶的資訊:
Email,通過SendEmail按鈕提供一個可用的email地址。
Fax,通過SendFax按鈕提供一個可用的傳真號碼,或者當email地址或者傳真號碼都無效的情況下,可以通過信函,通過SendLetter按鈕發送。
所以我們需要三個事件控制代碼來控制以下時間中的一個:
SendEmailEvent_handler
SendFax_handler
SendLetter_handler
我們需要一個被主應用程式調用的普通函數然後依次調用適當的時間控制代碼。
我們寫三個類,這三個類分別叫做Email,Fax和Letters。每個類都有自己發送資訊的方法。所以Email類有SendEmailMessage方法,Letter類有SendLetter方法,Fax類有SendFax方法來發送資訊。
我們需要的另一個類叫做MessageWatcher,這個類根據使用者的選擇調用適當的發送方法。
我們還要設計一個帶有三個按鈕的Form,每一個按鈕用來發送一種類型的訊息。當點擊SendEmailMessage按鈕時,產生一個事件,MessageWatcher類截取這個事件然後調用適當的事件控制代碼。在這個例子中調用的是Email類的SendEmailMessage方法。讓我們看一下是如何做到的。
MessageWatcher類和原始函數SendMessage如下。
//定義發送資訊的MessageWatcher類
public class MessageWatcher {
//定義訊息委託
public delegate void MessageHandler(object Sender, MessageInfo mi);
//定義一個事件
public event MessageHandler OnMessageSendRequest;
//這個函數響應發送的訊息
public void SendMessage(MessageInfo mr) {
//判斷是否被定義
if (OnMessageSendRequest !=null)
OnMessageSendRequest(this,mr);
}
注意OnMessageSendRequest(this,mr)函數帶有兩個參數並且不返回任何任何值。比較以下的SendEmailMessage函數。
public void SendEmailMessage(object Sender,MessageInfo ei){}
非常重要的是所有的函數形式和原始函數有相同的形式。所以SendEmailMessage和OnMessageSendRequest(this,mr)應該匹配參數和傳回型別。
現在的問題是MessageWatcher類如何決定它必須嗲用SendEmailMessage控制代碼呢?首先,Email類需要通知Watcher類它將要處理SendEmail事件並且在它的建構函式中傳入。文法如下:
public Email(MessageWatcher mw){
//儲存watcher引用
watcher=mw;
//告訴MessageWatcher類它將要處理SendEmail事件
watcher.OnMessageSendRequest+=new
MessageWatcher.MessageHandler(SendEmailMessage);
}
當Email類被執行個體化後,它通知MessageWatcher類OnMessageSendRequest事件發生了,它將處理這個事件。
你注意了沒有,MessageHandler是一個委託,我們很簡單的傳入了函數名SendEmailMessage。所以Email類通知MessageWatcher類他要處理SendEmailMessage事件。
最後,在主應用程式中我們寫了三行代碼。
//建立email類的執行個體
MessageWatcher mw=new MessageWatcher();
Email em=new Email(mw);mw.SendMessage(mi);
當點擊SendEmailMessage按鈕後,主應用程式建立一個MessageWatcher類和Email類的執行個體。Email類的建構函式傳入一個觀察者類的執行個體。當Email類被執行個體化後,它通知MessageWatcher執行個體它將要處理SendEmailMessage事件。最終,主應用程式調用MessageWatcher類的原始函數SendMessage,傳入MessageInfo資料結構。
SendMessage函數MessageWatcher類產生OnMessageSendRequest(this,mr)事件,因為Email類已經通知MessageWatcher類他將處理SendEmailMessage事件因此SendEmail函數來處理這個事件。
Email類利用.Net的System.Web類來發送emails。
Letter類使用Microsoft Word庫。因為這個庫是一個COM組件因此我使用System.Runtime.InteropServices。這個類使用模板文字來插入客戶文本。
有關作者lakshmi patil
三年軟體工程師工作經驗,工作領域asp.net,C#,xml,xslt,sqlserver,JavaScript和infragistics web控制項。