delegate經常被人用來與回調相比較,其實兩者在某種程度上由很多共同點。不過delegate有很多更加強大的地方。
首先,delegate中可以注冊任意多個回調,在一個delegate被調用的時候,已經注冊的過程將會被逐個調用。
其次,delegate允許注冊一個對象的方法,而不像C++中指可以使用靜態方法或者全局方法作為函數指針,提供了更多的靈活性,同時也暗示我們,delegate中按照某種方式保存了object的很多信息。
在C#2.0的匿名delegate中,我們甚至可以訪問當前匿名委托的上下文變量。接下來的文章中我們將會通過實際的例子,來看看強大的delegate。
首先,讓我們看看在C#1.2中的一個典型的委托的寫法。
public delegate void TheEvent(int a);
public void test()
{
TheEvent testdel1 = new TheEvent(del1);
testdel1(12);
}
public void del1(int x)
{
Console.WriteLine("output x : {0}", x);
}
現在我們可以寫成這樣:
public void test()
{
TheEvent testdel1 = del1;
testdel1(12);
}
或者將程序改寫為:
delegate void TheEvent2(int a);
public void test2()
{
int a = 12;
TheEvent ev2 = delegate(ref int x)
{ Console.WriteLine("output x : {0}", x); };
ev2( ref a);
}
比起1.2來,delegate的可讀性更好,但是似乎沒有本質的提高?慢著,讓我們看看下面的例子。
public static void test3()
{
int a = 12;
int y = 32;
TheEvent ev2 = delegate(ref int x)
{ Console.WriteLine("output x + y : {0}", x + y); };
ev2( ref a);
}
注意,匿名函數中的內容!x + y的值被正確的輸出了,而在1.2中,委托對於局部變量是沒有除參數外的訪問方式的。這樣做有些什麼好處呢?
讓我們看一個更加復雜的例子:
public static void test4()
{
int a = 12;
int y = 32;
TheEvent ev2 = delegate(ref int x)
{ Console.WriteLine("output x + y : {0}", x + y); Thread.Sleep(100); };
//ev2(ref a);
IAsyncResult ar = ev2.BeginInvoke(ref a,
delegate(IAsyncResult ar2)
{Console.Write("Operation finished: {0} on thread ID:{1}, is pool: {2}",ar2.IsCompleted,Thread.CurrentThread.GetHashCode(), Thread.CurrentThread.IsThreadPoolThread);}
, null);
Console.WriteLine("do some other calculations while counter thread is working");
Console.Write("work status : {0} Main Thread ID:{1}, is pool: {2}",
ar.IsCompleted,
Thread.CurrentThread.GetHashCode(),
Thread.CurrentThread.IsThreadPoolThread);
Thread.Sleep(500);
ev2.EndInvoke(ref a, ar);
}
這個例子中使用了系統線程池對於任務進行排隊,適合於IO或者計算密集型的操作的時候。使用匿名委托最大的好處在於可以完整地克隆當前運行空間上下文的可用變量,雖然這可能從另一個層面上也增加了同步的復雜度,所謂有得必有失。