在c#中,,event與delegate是兩個非常重要的概念。因?yàn)樵?/SPAN>Windows應(yīng)用程序中,,對事件的使用非常頻繁,,而事件的實(shí)現(xiàn)依賴于delegate。 下面是對網(wǎng)上一些比較好的關(guān)于delegage的資料的整理,,以及自己的一些想法,。 委托概述委托是一種安全地封裝方法的類型,它與 C 和 C++ 中的函數(shù)指針類似,。與 C 中的函數(shù)指針不同,,委托是面向?qū)ο蟮摹㈩愋桶踩暮捅kU(xiǎn)的,。委托的類型由委托的名稱定義,。 一旦為委托分配了方法,委托將與該方法具有完全相同的行為,。委托方法的使用可以像其他任何方法一樣,,具有參數(shù)和返回值,如下面的示例所示: public delegate void Del(string message); 與委托的簽名(由返回類型和參數(shù)組成)匹配的任何方法都可以分配給該委托,。這樣就可以通過編程方式來更改方法調(diào)用,,還可以向現(xiàn)有類中插入新代碼。只要知道委托的簽名,,便可以分配自己的委托方法,。 調(diào)用委托構(gòu)造委托對象時,通常提供委托將包裝的方法的名稱或使用匿名方法,。實(shí)例化委托后,,委托將把對它進(jìn)行的方法調(diào)用傳遞給方法。調(diào)用方傳遞給委托的參數(shù)被傳遞給方法,,來自方法的返回值(如果有)由委托返回給調(diào)用方,。這被稱為調(diào)用委托??梢詫⒁粋€實(shí)例化的委托視為被包裝的方法本身來調(diào)用該委托,。例如: // Create a method for a delegate. public static void DelegateMethod(string message) { System.Console.WriteLine(message); } // Instantiate the delegate. Del handler = DelegateMethod; // Call the delegate. handler("Hello World"); 將委托構(gòu)造為包裝實(shí)例方法時,該委托將同時引用實(shí)例和方法。除了它所包裝的方法外,,委托不了解實(shí)例類型,,所以只要任意類型的對象中具有與委托簽名相匹配的方法,委托就可以引用該對象,。將委托構(gòu)造為包裝靜態(tài)方法時,,它只引用方法。 回調(diào)由于實(shí)例化委托是一個對象,,所以可以將其作為參數(shù)進(jìn)行傳遞,,也可以將其賦值給屬性。這樣,,方法便可以將一個委托作為參數(shù)來接受,并且以后可以調(diào)用該委托,。這稱為異步回調(diào),,是在較長的進(jìn)程完成后用來通知調(diào)用方的常用方法。以這種方式使用委托時,,使用委托的代碼無需了解有關(guān)所用方法的實(shí)現(xiàn)方面的任何信息,。此功能類似于接口所提供的封裝。 回調(diào)的另一個常見用法是定義自定義的比較方法并將該委托傳遞給排序方法,。它允許調(diào)用方的代碼成為排序算法的一部分,。下面的示例方法使用 Del 類型作為參數(shù): public void MethodWithCallback(int param1, int param2, Del callback) { callback("The number is: " + (param1 + param2).ToString()); } 然后可以將上面創(chuàng)建的委托傳遞給該方法: MethodWithCallback(1, 2, handler); 在控制臺中將收到下面的輸出: The number is: 3 使用委托的好處委托允許類設(shè)計(jì)器分離類型聲明和實(shí)現(xiàn),。 在將委托用作抽象概念時,,MethodWithCallback 不需要直接調(diào)用控制臺 -- 設(shè)計(jì)它時無需考慮控制臺。MethodWithCallback 的作用只是準(zhǔn)備字符串并將該字符串傳遞給其他方法,。此功能特別強(qiáng)大,,因?yàn)槲械姆椒梢允褂萌我鈹?shù)量的參數(shù),。 將方法作為參數(shù)進(jìn)行引用的能力使委托成為定義回調(diào)方法的理想選擇。例如,,可以向排序算法傳遞對比較兩個對象的方法的引用,。分離比較代碼使得可以采用更通用的方式編寫算法。 如何使用委托1. 聲明委托 聲明一個新的委托類型,。每個委托類型都描述參數(shù)的數(shù)目和類型,,以及它可以封裝的方法的返回值類型。每當(dāng)需要一組新的參數(shù)類型或新的返回值類型時,,都必須聲明一個新的委托類型,。 2. 實(shí)例化委托 聲明了委托類型后,必須創(chuàng)建委托對象并使之與特定方法關(guān)聯(lián),。方法的簽名應(yīng)與委托定義的簽名一致,。 委托對象可以關(guān)聯(lián)靜態(tài)方法,也可以關(guān)聯(lián)非靜態(tài)方法,。 委托一旦創(chuàng)建,,它的關(guān)聯(lián)方法就不能更改,;委托對象是不可變的。 3. 調(diào)用委托 創(chuàng)建委托對象后,,通常將委托對象傳遞給將調(diào)用該委托的其他代碼,。通過委托對象的名稱(后面跟著要傳遞給委托的參數(shù),括在括號內(nèi))調(diào)用委托對象,。 下面是一個示例: using System; public class SamplesDelegate { // Declares a delegate for a method that takes in an int and returns a String. public delegate String myMethodDelegate(int myInt);
// Defines some methods to which the delegate can point. public class mySampleClass { // Defines an instance method. public String myStringMethod(int myInt) { if (myInt > 0) return ("positive"); if (myInt < 0) return ("negative"); return ("zero"); }
// Defines a static method. public static String mySignMethod(int myInt) { if (myInt > 0) return ("+"); if (myInt < 0) return ("-"); return (""); } }
public static void Main() { // Creates one delegate for each method. mySampleClass mySC = new mySampleClass(); myMethodDelegate myD1 = new myMethodDelegate(mySC.myStringMethod); myMethodDelegate myD2 = new myMethodDelegate(mySampleClass.mySignMethod);
// Invokes the delegates. Console.WriteLine("{0} is {1}; use the sign /"{2}/".", 5, myD1(5), myD2(5)); Console.WriteLine("{0} is {1}; use the sign /"{2}/".", -3, myD1(-3), myD2(-3)); Console.WriteLine("{0} is {1}; use the sign /"{2}/".", 0, myD1(0), myD2(0)); } }
/* This code produces the following output: 5 is positive; use the sign "+". -3 is negative; use the sign "-". 0 is zero; use the sign "". */
多路廣播調(diào)用委托時,,它可以調(diào)用多個方法。這稱為多路廣播,。若要向委托的方法列表(調(diào)用列表)中添加額外的方法,,只需使用加法運(yùn)算符或加法賦值運(yùn)算符(“+”或“+=”)添加兩個委托。例如: MethodClass obj = new MethodClass(); Del d1 = obj.Method1; Del d2 = obj.Method2; Del d3 = DelegateMethod;
//Both types of assignment are valid. Del allMethodsDelegate = d1 + d2; allMethodsDelegate += d3; 此時,,allMethodsDelegate 在其調(diào)用列表中包含三個方法 -- Method1,、Method2 和 DelegateMethod。原來的三個委托 d1,、d2 和 d3 保持不變,。調(diào)用 allMethodsDelegate 時,將按順序調(diào)用所有這三個方法,。如果委托使用引用參數(shù),,則引用將依次傳遞給三個方法中的每個方法,由一個方法引起的更改對下一個方法是可見的,。如果任一方法引發(fā)了異常,,而在該方法內(nèi)未捕獲該異常,則該異常將傳遞給委托的調(diào)用方,,并且不再對調(diào)用列表中后面的方法進(jìn)行調(diào)用,。如果委托具有返回值和/或輸出參數(shù),它將返回最后調(diào)用的方法的返回值和參數(shù),。若要從調(diào)用列表中移除方法,,請使用減法運(yùn)算符或減法賦值運(yùn)算符(“-”或“-=”)。例如: //remove Method1 allMethodsDelegate -= d1;
// copy AllMethodsDelegate while removing d2 Del oneMethodDelegate = allMethodsDelegate - d2; 多路廣播委托廣泛用于事件處理中,。事件源對象向已注冊接收該事件的接收方對象發(fā)送事件通知,。為了為事件注冊,接收方創(chuàng)建了旨在處理事件的方法,,然后為該方法創(chuàng)建委托并將該委托傳遞給事件源,。事件發(fā)生時,源將調(diào)用委托,。然后,,委托調(diào)用接收方的事件處理方法并傳送事件數(shù)據(jù)。給定事件的委托類型由事件源定義。 本示例演示如何組合多路廣播委托,。委托對象的一個用途在于,,可以使用 + 運(yùn)算符將它們分配給一個要成為多路廣播委托的委托實(shí)例。組合的委托可調(diào)用組成它的那兩個委托,。只有相同類型的委托才可以組合,。 - 運(yùn)算符可用來從組合的委托移除組件委托。 delegate void Del(string s);
class TestClass { static void Hello(string s) { System.Console.WriteLine(" Hello, {0}!", s); }
static void Goodbye(string s) { System.Console.WriteLine(" Goodbye, {0}!", s); }
static void Main() { Del a, b, c, d;
// Create the delegate object a that references // the method Hello: a = Hello;
// Create the delegate object b that references // the method Goodbye: b = Goodbye;
// The two delegates, a and b, are composed to form c: c = a + b;
// Remove a from the composed delegate, leaving d, // which calls only the method Goodbye: d = c - a;
System.Console.WriteLine("Invoking delegate a:"); a("A"); System.Console.WriteLine("Invoking delegate b:"); b("B"); System.Console.WriteLine("Invoking delegate c:"); c("C"); System.Console.WriteLine("Invoking delegate d:"); d("D"); } } 他人對Delegate的總結(jié)下面是sam1111在他的博客文章《C#中的delegate和event》中對delegate的總結(jié),,覺得很好,。 文章網(wǎng)址:http://blog.csdn.net/sam1111/archive/2002/04/15/9773.aspx delegate是C#中的一種類型,它實(shí)際上是一個能夠持有對某個方法的引用的類,。與其它的類不同,,delegate類能夠擁有一個簽名(signature),并且它只能持有與它的簽名相匹配的方法的引用,。它所實(shí)現(xiàn)的功能與C/C++中的函數(shù)指針十分相似,。它允許傳遞一個類A的方法m給另一個類B的對象,使得類B的對象能夠調(diào)用這個方法m,。但與函數(shù)指針相比,,delegate有許多函數(shù)指針不具備的優(yōu)點(diǎn),。首先,,函數(shù)指針只能指向靜態(tài)函數(shù),而delegate既可以引用靜態(tài)函數(shù),,又可以引用非靜態(tài)成員函數(shù),。在引用非靜態(tài)成員函數(shù)時,delegate不但保存了對此函數(shù)入口指針的引用,,而且還保存了調(diào)用此函數(shù)的類實(shí)例的引用,。其次,與函數(shù)指針相比,,delegate是面向?qū)ο?、類型安全、可靠的受控?/SPAN>managed)對象,。也就是說,,runtime能夠保證delegate指向一個有效的方法,你無須擔(dān)心delegate會指向無效地址或者越界地址,。 自己的理解“delegate是C#中的一種類型,。”delegate與class是類似的,,class定義一種類型,,delegate也定義一種類型。class可以定義各種各樣的類,如classA,、classB,,而delegate可以定義各種各樣的代理,如delegate1,,delegate2,。與class不同的是,delegate的定義沒有字段,、屬性,、方法等,只有簽名(返回值及參數(shù)),。 “它實(shí)際上是一個能夠持有對某個方法的引用的類,。”delegate對象可以持有對某個方法的引用,,這個方法的簽名必須與代理類型的簽名一致(這就是“delegate定義回調(diào)方法的接口”這一說法的原由),。代理對象持有對這個方法的引用,當(dāng)調(diào)用代理對象時,,即實(shí)現(xiàn)對這個方法的調(diào)用,。之所以能通過調(diào)用代理對象來實(shí)現(xiàn)對方法的調(diào)用,是因?yàn)樵趯?shí)例化代理對象時,,把傳入方法的地址賦給了代理對象,,使得當(dāng)調(diào)用代理對象時,內(nèi)存中的指令指針即指向傳入方法的入口,,執(zhí)行傳入方法的方法體,。 利用代理來實(shí)現(xiàn)多路廣播時,即把多個方法的引用(即內(nèi)存地址)保存到代理的方法引用隊(duì)列,。調(diào)用代理對象時,,根據(jù)代理對象的方法引用隊(duì)列,內(nèi)存中的指令指針即逐個指向每個方法的入口,,按次序執(zhí)行每個方法的方法體,。 事件的發(fā)布與訂閱。所謂事件,,就是指當(dāng)某個特定的事情發(fā)生時,,類或?qū)ο笸ㄟ^事件通知關(guān)注此事情的類或?qū)ο蟆?/SPAN>發(fā)送(或引發(fā))事件的類稱為“發(fā)行者”,接收(或處理)事件的類稱為“訂戶”,。事件的實(shí)現(xiàn)是依賴于代理的,。事件的實(shí)現(xiàn)原理即,訂戶把響應(yīng)事件的方法傳遞給發(fā)行者,,當(dāng)特定的事情發(fā)生時,,發(fā)行者能夠調(diào)用這些響應(yīng)事件的方法,。在代碼級的實(shí)現(xiàn)即,發(fā)行者定義一個delegate類型,,提供一個public的delegate對象作為字段或?qū)傩?;訂戶(可以是多個)通過將響應(yīng)事件的方法傳遞給delegate類型來實(shí)例化一個delegate對象,并通過+=運(yùn)算符,,將delegate對象賦值給發(fā)行者的delegate對象,,實(shí)際上就是多路廣播。當(dāng)特定的事情發(fā)生時,,發(fā)行者調(diào)用delegate對象,,即調(diào)用所有訂戶的響應(yīng)事件的方法。 |
|