在c#中,event與delegate是兩個非常重要的概念。因為在Windows應用程式中,對事件的使用非常頻繁,而事件的實現依賴於delegate。
下面是對網上一些比較好的關於delegage的資料的整理,以及自己的一些想法。
Delegate是什嗎?
Delegate中文翻譯為“委託”。Msdn中對Delegate的解釋如下:
C#中的委託類似於C或C++中的函數指標。使用委託使程式員可以將方法引用封裝在委派物件內。然後可以將該委派物件傳遞給可調用所引用方法的代碼,而不必在編譯時間知道將調用哪個方法。與C或C++中的函數指標不同,委託是物件導向、型別安全的,並且是安全的。
一旦為委託分配了方法,委託將與該方法具有完全相同的行為。委託方法的使用可以像其他任何方法一樣,具有參數和傳回值,如下面的樣本所示:
public delegate void Del(string message);
與委託的簽名(由傳回型別和參數組成)匹配的任何方法都可以分配給該委託。這樣就可以通過編程方式來更改方法調用,還可以向現有類中插入新代碼。只要知道委託的簽名,便可以分配自己的委託方法。
調用委託
構造委派物件時,通常提供委託將封裝的方法的名稱或使用匿名方法。執行個體化委託後,委託將把對它進行的方法調用傳遞給方法。調用方傳遞給委託的參數被傳遞給方法,來自方法的傳回值(如果有)由委託返回給調用方。這被稱為調用委託。可以將一個執行個體化的委託視為被封裝的方法本身來調用該委託。例如:
// 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");
將委託構造為封裝執行個體方法時,該委託將同時引用執行個體和方法。除了它所封裝的方法外,委託不瞭解執行個體類型,所以只要任意類型的對象中具有與委託簽名相匹配的方法,委託就可以引用該對象。將委託構造為封裝靜態方法時,它只引用方法。
回調
由於執行個體化委託是一個對象,所以可以將其作為參數進行傳遞,也可以將其賦值給屬性。這樣,方法便可以將一個委託作為參數來接受,並且以後可以調用該委託。這稱為非同步回調,是在較長的進程完成後用來通知調用方的常用方法。以這種方式使用委託時,使用委託的代碼無需瞭解有關所用方法的實現方面的任何資訊。此功能類似於介面所提供的封裝。
回調的另一個常見用法是定義自訂的比較方法並將該委託傳遞給排序方法。它允許調用方的代碼成為排序演算法的一部分。下面的樣本方法使用 Del 類型作為參數:
public void MethodWithCallback(int param1, int param2, Del callback){callback("The number is: " + (param1 + param2).ToString());}
然後可以將上面建立的委託傳遞給該方法:
MethodWithCallback(1, 2, handler);
在控制台中將收到下面的輸出:
The number is: 3
使用委託的好處
委託允許類別設計工具分離型別宣告和實現。
在將委託用作抽象概念時,MethodWithCallback 不需要直接調用控制台 -- 設計它時無需考慮控制台。MethodWithCallback 的作用只是準備字串並將該字串傳遞給其他方法。此功能特彆強大,因為委託的方法可以使用任意數量的參數。
將方法作為參數進行引用的能力使委託成為定義回調方法的理想選擇。例如,可以向排序演算法傳遞對比較兩個對象的方法的引用。分離比較代碼使得可以採用更通用的方式編寫演算法。
如何使用委託
1. 聲明委託
聲明一個新的委託類型。每個委託類型都描述參數的數目和類型,以及它可以封裝的方法的傳回值類型。每當需要一組新的參數類型或新的傳回值類型時,都必須聲明一個新的委託類型。
2. 執行個體化委託
聲明了委託類型後,必須建立委派物件並使之與特定方法關聯。方法的簽名應與委託定義的簽名一致。
委派物件可以關聯靜態方法,也可以關聯非靜態方法。
委託一旦建立,它的關聯方法就不能更改;委派物件是不可變的。
3. 調用委託
建立委派物件後,通常將委派物件傳遞給將調用該委託的其他代碼。通過委派物件的名稱(後面跟著要傳遞給委託的參數,括在括弧內)調用委派物件。
下面是一個樣本:
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 "".*/
多路廣播
調用委託時,它可以調用多個方法。這稱為多路廣播。若要向委託的方法列表(調用列表)中添加額外的方法,只需使用加法運算子或加法賦值運算子(“+”或“+=”)添加兩個委託。例如:
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 在其調用列表中包含三個方法 -- Method1、Method2 和 DelegateMethod。原來的三個委託 d1、d2 和 d3 保持不變。調用 allMethodsDelegate 時,將按順序調用所有這三個方法。如果委託使用引用參數,則引用將依次傳遞給三個方法中的每個方法,由一個方法引起的更改對下一個方法是可見的。如果任一方法引發了異常,而在該方法內未捕獲該異常,則該異常將傳遞給委託的調用方,並且不再對調用列表中後面的方法進行調用。如果委託具有傳回值和/或輸出參數,它將返回最後調用的方法的傳回值和參數。若要從調用列表中移除方法,請使用減法運算子或減法賦值運算子(“-”或“-=”)。例如:
//remove Method1allMethodsDelegate -= d1;// copy AllMethodsDelegate while removing d2Del oneMethodDelegate = allMethodsDelegate - d2;
多路廣播委託廣泛用於事件處理中。事件來源對象向登入接收該事件的接收方對象發送事件通知。為了為事件註冊,接收方建立了旨在處理事件的方法,然後為該方法建立委託並將該委託傳遞給事件來源。事件發生時,源將調用委託。然後,委託調用接收方的事件處理方法並傳送事件數目據。給定事件的委託類型由事件來源定義。
本樣本示範如何組合多路廣播委託。委派物件的一個用途在於,可以使用 + 運算子將它們分配給一個要成為多路廣播委託的委託執行個體。組合的委託可調用組成它的那兩個委託。只有相同類型的委託才可以組合。
運算子可用來從組合的委託移除組件委託。
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的總結
delegate是C#中的一種類型,它實際上是一個能夠持有對某個方法的引用的類。與其它的類不同,delegate類能夠擁有一個簽名(signature),並且它只能持有與它的簽名相匹配的方法的引用。它所實現的功能與C/C++中的函數指標十分相似。它允許傳遞一個類A的方法m給另一個類B的對象,使得類B的對象能夠調用這個方法m。但與函數指標相比,delegate有許多函數指標不具備的優點。首先,函數指標只能指向靜態函數,而delegate既可以引用靜態函數,又可以引用非靜態成員函數。在引用非靜態成員函數時,delegate不但儲存了對此函數入口指標的引用,而且還儲存了調用此函數的類執行個體的引用。其次,與函數指標相比,delegate是物件導向、型別安全、可靠的受控(managed)對象。也就是說,runtime能夠保證delegate指向一個有效方法,你無須擔心delegate會指向無效地址或者越界地址。
自己對Delegate的理解
“delegate是C#中的一種類型。”delegate與class是類似的,class定義一種類型,delegate也定義一種類型。class可以定義各種各樣的類,如classA、classB,而delegate可以定義各種各樣的代理,如delegate1,delegate2。與class不同的是,delegate的定義沒有欄位、屬性、方法等,只有簽名(傳回值及參數)。
“它實際上是一個能夠持有對某個方法的引用的類。”delegate對象可以持有對某個方法的引用,這個方法的簽名必須與代理類型的簽名一致(這就是“delegate定義回調方法的介面”這一說法的原由)。代理對象持有對這個方法的引用,當調用代理對象時,即實現對這個方法的調用。之所以能通過調用代理對象來實現對方法的調用,是因為在執行個體化代理對象時,把傳入方法的地址賦給了代理對象,使得當調用代理對象時,記憶體中的指令指標即指向傳入方法的入口,執行傳入方法的方法體。
利用代理來實現多路廣播時,即把多個方法的引用(即記憶體位址)儲存到代理的方法引用隊列。調用代理對象時,根據代理對象的方法引用隊列,記憶體中的指令指標即逐個指向每個方法的入口,按次序執行每個方法的方法體。
事件的發布與訂閱。所謂事件,就是指當某個特定的事情發生時,類或對象通過事件通知關注此事情的類或對象。發送(或引發)事件的類稱為“發行者”,接收(或處理)事件的類稱為“訂戶”。事件的實現是依賴於代理的。事件的實現原理即,訂戶把響應事件的方法傳遞給發行者,當特定的事情發生時,發行者能夠調用這些響應事件的方法。在代碼級的實現即,發行者定義一個delegate類型,提供一個public的delegate對象作為欄位或屬性;訂戶(可以是多個)通過將響應事件的方法傳遞給delegate類型來執行個體化一個delegate對象,並通過+=運算子,將delegate對象賦值給發行者的delegate對象,實際上就是多路廣播。當特定的事情發生時,發行者調用delegate對象,即調用所有訂戶的響應事件的方法。
以上所述是小編給大家介紹的深入理解C#中的Delegate ,希望對大家有所協助,如果大家有任何疑問請給我留言,小編會及時回複大家的。在此也非常感謝大家對雲棲社區網站的支援!