委托類型定義
C#編譯器處理委托時先自動產生一個派生自SystemMulticastDelegate的密封類這個類與它的基類SystemDelegate一起為委托提供必要的基礎設施以維護以後將要調用的方法列表它含有個編譯器生成的方法這個方法的參數與返回值基於委托的聲明
public sealed class DelegateName
System
MulticastDelegate
{
public DReturnType Invoke (DParams);
public IAsyncResult BeginInvoke(DParams
AsyncCallback cb
object state);
public DReturnType EndInvoke(DRefAndOutParams
IAsyncResult result);
}
委托類型實例
某種類型的實例方法和可分配給該類型的目標對象
某種類型的實例方法(包含在形參表中公開的隱藏 this 參數)該委托稱為開放式實例委托
靜態方法
靜態方法和可分配給該方法的第一個參數的目標對象該委托稱為通過其第一個參數關閉
何時使用委托而不使用接口
委托和接口都允許類設計器分離類型聲明和實現給定的接口可由任何類或結構繼承和實現;可以為任何類中的方法創建委托前提是該方法符合委托的方法簽名接口引用或委托可由不了解實現該接口或委托方法的類的對象使用既然存在這些相似性那麼類設計器何時應使用委托何時又該使用接口呢?
在以下情況中使用委托
當使用事件設計模式時
當封裝靜態方法可取時
當調用方不需要訪問實現該方法的對象中的其他屬性方法或接口時
需要方便的組合
當類可能需要該方法的多個實現時
在以下情況中使用接口
當存在一組可能被調用的相關方法時
當類只需要方法的單個實現時
當使用接口的類想要將該接口強制轉換為其他接口或類類型時
當正在實現的方法鏈接到類的類型或標識時例如比較方法
聲明委托
聲明一個新的委托類型每個委托類型都描述參數的數目和類型以及它可以封裝的方法的返回值類型每當需要一組新的參數類型或新的返回值類型時都必須聲明一個新的委托類型
public delegate void ProcessBookDelegate(Book book);
實例化委托
聲明了委托類型後必須創建委托對象並使之與特定方法關聯
在上面的示例中這是通過將 PrintTitle 方法傳遞給ProcessPaperbackBooks 方法來完成的
bookDBProcessPaperbackBooks(PrintTitle);
這將創建與靜態方法 TestPrintTitle 關聯的新委托對象類似地對象 totaller 的非靜態方法 AddBookToTotal 是按如下方式傳遞的
bookDBProcessPaperbackBooks(totallerAddBookToTotal);
調用委托
創建委托對象後通常將委托對象傳遞給將調用該委托的其他代碼通過委托對象的名稱(後面跟著要傳遞給委托的參數括在括號內)調用委托對象
processBook(b);
多路廣播委托
本示例演示如何組合多路廣播委托委托對象的一個用途在於可以使用 + 運算符將它們分配給一個要成為多路廣播委托的委托實例組合的委托可調用組成它的那兩個委托只有相同類型的委托才可以組合 運算符可用來從組合的委托移除組件委托
根據CIL代碼可以發現+=操作符實際上是轉換為了一個靜態的DelegateCombine()方法的調用=操作符實際上是轉換為了一個靜態的DelegateRemove()方法的調用
delegate void Del(string s);
class TestClass
{
static void Hello(string s)
{
SystemConsoleWriteLine( Hello {}! s);
}
static void Goodbye(string s)
{
SystemConsoleWriteLine( Goodbye {}! s);
}
static void Main()
{
Del a b c d;
a = Hello;
b = Goodbye;
c = a + b;
d = c a;
SystemConsoleWriteLine(Invoking delegate a:);
a(A);
SystemConsoleWriteLine(Invoking delegate b:);
b(B);
SystemConsoleWriteLine(Invoking delegate c:);
c(C);
SystemConsoleWriteLine(Invoking delegate d:);
d(D);
}
}
匿名方法
在 之前的 C# 版本中聲明委托的唯一方法是使用命名方法C# 引入了匿名方法要將代碼塊傳遞為委托參數創建匿名方法則是唯一的方法
如果使用匿名方法則不必創建單獨的方法因此減少了實例化委托所需的編碼系統開銷例如如果創建方法所需的系統開銷是不必要的在委托的位置指定代碼塊就非常有用啟動新線程即是一個很好的示例無需為委托創建更多方法線程類即可創建一個線程並且包含該線程執行的代碼
buttonClick += delegate(SystemObject o SystemEventArgs e)
{ SystemWindowsFormsMessageBoxShow(Click!); };
delegate void Del(int x);
Del d = delegate(int k) { /* */ };
void StartThread()
{
SystemThreadingThread t = new SystemThreadingThread
(delegate()
{
SystemConsoleWrite(Hello );
SystemConsoleWriteLine(World!);
});
tStart();
}
匿名方法可以訪問定義他們的方法的本地變量即匿名方法的外部變量
匿名方法不能訪問定義方法中的ref或out參數
匿名方法中的本地變量不能與外部方法的本地變量重名
匿名方法可以訪問外部類作用域中的實例變量或靜態變量
委托中的協變和逆變
將方法簽名與委托類型匹配時協變和逆變為您提供了一定程度的靈活性
協變也稱為寬松委托協變允許構造一個委托指向返回類及其相關繼承體系的方法
逆變允許方法具有的派生參數類型比委托類型中的更少
協變
本示例演示如何將委托與具有返回類型的方法一起使用這些返回類型派生自委托簽名中的返回類型由 SecondHandler 返回的數據類型是 Dogs 類型它是由委托中定義的 Mammals 類型派生的
Foo<父類> = Foo<子類>
class Mammals{}
class Dogs : Mammals{}
class Program
{
public delegate Mammals HandlerMethod();
public static Mammals FirstHandler(){return null;}
public static Dogs SecondHandler(){return null;}
static void Main()
{
HandlerMethod handler = FirstHandler;
HandlerMethod handler = SecondHandler;
}
}
逆變
本示例演示如何將委托與具有某個類型的參數的方法一起使用這些參數是委托簽名參數類型的基類型通過逆變以前必須使用若干個不同處理程序的地方現在只要使用一個事件處理程序即可;如現在可以創建一個接收 EventArgs 輸入參數的事件處理程序然後可以將該處理程序與發送 MouseEventArgs 類型(作為參數)的 ButtonMouseClick 事件一起使用也可以將該處理程序與發送 KeyEventArgs 參數的 TextBoxKeyDown 事件一起使用
Foo<子類> = Foo<父類>
SystemDateTime lastActivity;
public Form()
{
InitializeComponent();
lastActivity = new SystemDateTime();
thistextBoxKeyDown += thisMultiHandler;
thisbuttonMouseClick += thisMultiHandler;
}
private void MultiHandler(object sender SystemEventArgs e)
{
lastActivity = SystemDateTimeNow;
}
泛型委托
委托可以定義自己的類型參數引用泛型委托的代碼可以指定類型參數以創建已關閉的構造類型就像實例化泛型類或調用泛型方法一樣
public delegate void Del<T>(T item);
public static void Notify(int i) { }
Del<int> m = new Del<int>(Notify);
方法組轉換
C#提供了一種叫做方法組轉換的簡便方法該特性允許我們在調用以委托作為參數的方法時直接提供方法的名稱而不用創建委托對象此功能適用於具體委托類型和泛型委托類型並使您可以使用如下簡化的語法寫入上一行
Del<int> m = Notify;
泛型類內部定義委托使用泛型類類型參數
在泛型類內部定義的委托使用泛型類類型參數的方式可以與類方法所使用的方式相同
class Stack<T>
{
T[] items;
int index;
public delegate void StackDelegate(T[] items);
}
引用委托的代碼必須指定包含類的類型變量
private static void DoWork(float[] items) { }
public static void TestStack()
{
Stack<float> s = new Stack<float>();
Stack<float>StackDelegate d = DoWork;
}
泛型委托取代傳統裝箱拆箱委托
根據典型設計模式定義事件時泛型委托尤其有用因為發送方參數可以為強類型不再需要強制轉換成 Object或反向強制轉換
delegate void StackEventHandler<T U>(T sender U eventArgs);
class Stack<T>
{
public class StackEventArgs : SystemEventArgs { }
public event StackEventHandler<Stack<T> StackEventArgs> stackEvent;
protected virtual void OnStackChanged(StackEventArgs a)
{
stackEvent(this a);
}
}
class SampleClass
{
public void HandleStackChange<T>(Stack<T> stack Stack<T>StackEventArgs args) { }
}
public static void Test()
{
Stack<double> s = new Stack<double>();
SampleClass o = new SampleClass();
sstackEvent += oHandleStackChange;
}
From:http://tw.wingwit.com/Article/program/net/201311/11939.html