在c#中,event與delegate是兩個很是重要的概念。由於在Windows應用程序中,對事件的使用很是頻繁,而事件的實現依賴於delegate。
下面是對網上一些比較好的關於delegage的資料的整理,以及本身的一些想法。
委託概述
委託是一種安全地封裝方法的類型,它與 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 在其調用列表中包含三個方法 -- Method一、Method2 和 DelegateMethod。原來的三個委託 d一、d2 和 d3 保持不變。調用 allMethodsDelegate 時,將按順序調用全部這三個方法。若是委託使用引用參數,則引用將依次傳遞給三個方法中的每一個方法,由一個方法引發的更改對下一個方法是可見的。若是任一方法引起了異常,而在該方法內未捕獲該異常,則該異常將傳遞給委託的調用方,而且再也不對調用列表中後面的方法進行調用。若是委託具備返回值和/或輸出參數,它將返回最後調用的方法的返回值和參數。若要從調用列表中移除方法,請使用減法運算符或減法賦值運算符(「-」或「-=」)。例如:
//remove Method1
allMethodsDelegate -= d1;
// copy AllMethodsDelegate while removing d2
Del 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的總結
下面是sam1111在他的博客文章《C#中的delegate和event》中對delegate的總結,以爲很好。
文章網址:http://blog.csdn.net/sam1111/archive/2002/04/15/9773.aspx
delegate是C#中的一種類型,它其實是一個可以持有對某個方法的引用的類。與其它的類不一樣,delegate類可以擁有一個簽名(signature),而且它只能持有與它的簽名相匹配的方法的引用。它所實現的功能與C/C++中的函數指針十分類似。它容許傳遞一個類A的方法m給另外一個類B的對象,使得類B的對象可以調用這個方法m。但與函數指針相比,delegate有許多函數指針不具有的優勢。首先,函數指針只能指向靜態函數,而delegate既能夠引用靜態函數,又能夠引用非靜態成員函數。在引用非靜態成員函數時,delegate不但保存了對此函數入口指針的引用,並且還保存了調用此函數的類實例的引用。其次,與函數指針相比,delegate是面向對象、類型安全、可靠的受控(managed)對象。也就是說,runtime可以保證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對象,即調用全部訂戶的響應事件的方法。算法