【Delegate (CLI)】
委托（delegate）是一种类型安全的函数指针，用于通用语言运行库(CLI)。在C#中，delegate是一种class，包装了一个或多个函数指针及绑定的类实例。Delegate用来实现函数回调与事件接收（event listener）。Delegate对象可以作为参数传递给其他函数，以引用（referenced）封装在delegate对象中的函数，而无需在编译时刻就绑定被调用函数。
一旦为委托分配了函数方法，委托将与该函数方法具有完全相同的行为。 与委托的类型特征（由返回类型和参数组成）匹配的任何方法都可以分配给该委托。
== C#例子 ==
声明一个delegate类型，称作SendMessageDelegate, 以一个Message类型为参数，返回类型为void:
delegate void SendMessageDelegate(Message message);
下述代码定义了一个函数，以一个实例化的delegate类型作为形参:
void SendMessage(SendMessageDelegate sendMessageDelegateReference)
{
// call the delegate and any other chained delegates synchronously
sendMessageDelegateReference(new Message("hello this is a sample message"));
}
将要封装入delegate中的一个函数的定义：
void HandleSendMessage(Message message)
{
// the implementation for the Sender and Message classes are not relevant to this example
Sender.Send(message);
}
函数SendMessage，以SendMessageDelegate作为委托的实例作为实参：
SendMessage(new SendMessageDelegate(HandleSendMessage));
委托实例可以封装多个函数：
delegateType de1=fun1;
delegateType de2=fun2;
delegateType de3=de1+de2;//de3中有fun1、fun2两个函数的引用
== 技术实现细节 ==
“委托”作为类，继承自System.MulticastDelegate（抽象类）。“委托”至少0个参数，至多32个参数，可以无返回值，也可以指定返回值类型。可以认为包含：一个类对象实例的地址（Target属性），该类的一个方法的地址（Method属性），以及另一个“委托”实例的引用（reference）。因此引用一个“委托”对象，可能实际上引用了多个“委托”的实例。“委托”对象被调用时，依次调用里面的多个“委托”的实例。这对于事件驱动的程序比较有用。
如果“委托”封装了一个静态函数，则其内部的绑定的类对象地址为null。
可以通过Delegate类的GetInvocationList()取出这些委托，并查看其Target和Method属性，获取所引用的方法名等信息。
==泛型委托==
关键字Action定义了无返回值的泛型委托。Action从0个参数，至多16个参数。例如：
public Action cwdelegate; //定义一个委托cwdelegate，参数是int，无返回值。
关键字Func定义了有返回值的泛型委托。从0个参数，至多16个参数。其中最右侧的泛型类型是返回值类型。
关键字Predict定义了返回值为bool的泛型委托。
在委托实例前，不加event，那这个委托就是一个普通的委托，可以在别的函数中调用，也可以直接用对象调用。但加上event关键字之后，只能用成员函数调用
==用途==
一个常用的用途是事件处理。CLI定义了控件的标准的事件处理函数是一个“委托”，声明如下：
public delegate void EventHandler(object sender, EventArgs e)
这里第一个参数是发出该事件的控件的基类型object；后一个参数是事件数据的基类型EventArgs。以Button控件类为例，它有一个属性Click，定义为一个EventHandle类型的delegate:
public event EventHandle Click;
至此，我们对一个具体的button实例变量，可以给它的Click事件追加上一个或多个事件处理函数：
this.button1.Click += new System.EventHandler(this.button1_click);
对于Visual Basic，上述语句的写法是：
AddHandler button1.click, AddressOf button1_Click
==异步执行委托==
直接调用一个委托，是同步阻塞放式。可以用委托类的有两个方法BeginInvoke()和EndInvoke()异步执行委托方法。
BeginInvoke()用线程池的线程异步执行委托中的方法，且主调线程立即返回。前提是委托中只能有一个方法。BeginInvoke()返回IAsyncResult对象。BeginInvoke()除了包含委托中方法的参数之外，还包含两个额外参数：
*System.AsyncCallback类型的回调方法的委托对象。在BeginInvoke()启动的委托方法执行完毕后，自动调用该回调方法。
*一个object，作为IAsyncResult的AsyncState属性。可自由定制、使用。
上述System.AsyncCallback回调方法的参数为IAsyncResult类型，由该参数可以获取到AsyncState属性、AsyncMethodCaller属性（即原委托对象），以及当前异步执行状态。用获取的委托AsyncMethodCaller调用EndInvoke()，可获取线程池中的执行线程的id、委托方法的返回结果，并最终释放执行线程。
例如：
using System;
using System.IO;
using System.Threading;
using System.Runtime.Remoting.Messaging;
namespace Examples.AdvancedProgramming.AsynchronousOperations
{
public class AsyncMain
{
static void Main()
{
// Create an instance of the test class.
AsyncDemo ad = new AsyncDemo();
// Create the delegate.
AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);
// The threadId parameter of TestMethod is an out parameter, so
// its input value is never used by TestMethod. Therefore, a dummy
// variable can be passed to the BeginInvoke call. If the threadId
// parameter were a ref parameter, it would have to be a class-
// level field so that it could be passed to both BeginInvoke and
// EndInvoke.
int dummy = 0;
// Initiate the asynchronous call, passing three seconds (3000 ms)
// for the callDuration parameter of TestMethod; a dummy variable
// for the out parameter (threadId); the callback delegate; and
// state information that can be retrieved by the callback method.
// In this case, the state information is a string that can be used
// to format a console message.
IAsyncResult result = caller.BeginInvoke(3000,
out dummy,
new AsyncCallback(CallbackMethod),
"The call executed on thread {0}, with return value \"{1}\".");
Console.WriteLine("The main thread {0} continues to execute...",
Thread.CurrentThread.ManagedThreadId);
// The callback is made on a ThreadPool thread. ThreadPool threads
// are background threads, which do not keep the application running
// if the main thread ends. Comment out the next line to demonstrate
// this.
Thread.Sleep(40000);
Console.WriteLine("The main thread ends.");
}
// The callback method must have the same signature as the
// AsyncCallback delegate.
static void CallbackMethod(IAsyncResult ar)
{
// Retrieve the delegate.
AsyncResult result = (AsyncResult)ar;
AsyncMethodCaller caller = (AsyncMethodCaller)result.AsyncDelegate;
// Retrieve the format string that was passed as state
// information.
string formatString = (string)ar.AsyncState;
// Define a variable to receive the value of the out parameter.
// If the parameter were ref rather than out then it would have to
// be a class-level field so it could also be passed to BeginInvoke.
int threadId = 0;
// Call EndInvoke to retrieve the results.
string returnValue = caller.EndInvoke(out threadId, ar);
// Use the format string to format the output message.
Console.WriteLine(formatString, threadId, returnValue);
}
}
}
/* This example produces output similar to the following:
The main thread 1 continues to execute...
Test method begins.
The call executed on thread 3, with return value "My call time was 3000.".
The main thread ends.
*/
namespace Examples.AdvancedProgramming.AsynchronousOperations
{
public class AsyncDemo
{
// The method to be executed asynchronously.
public string TestMethod(int callDuration, out int threadId)
{
Console.WriteLine("Test method begins.");
Thread.Sleep(callDuration);
threadId = Thread.CurrentThread.ManagedThreadId;
return String.Format("My call time was {0}.", callDuration.ToString());
}
}
// The delegate must have the same signature as the method
// it will call asynchronously.
public delegate string AsyncMethodCaller(int callDuration, out int threadId);
}
第二种方法是借助返回的IAsyncHandle的WaitHandle属性的WaitOne()方法实现主线程和异步线程同步后，再调用EndInvoke方法：
using System;
using System.Threading;
namespace sample
{
class AsyncDemo
{
public string TestMethod(int callDuration, out int threadid)
{
Console.WriteLine("Test Method begins");
Thread.Sleep(callDuration);
threadid = AppDomain.GetCurrentThreadId();
return "MyCallTime was" + callDuration.ToString();
}
}
public delegate string AsyncDelegate(int callDuration, out int threadid);
class Program
{
static void Main(string args)
{
int threadID;
AsyncDemo ad = new AsyncDemo();
AsyncDelegate andl = new AsyncDelegate(ad.TestMethod);
IAsyncResult ar = andl.BeginInvoke(3000, out threadID, null, null);
Thread.Sleep(10);
Console.WriteLine("Main Thread {0} Does Some Work", AppDomain.GetCurrentThreadId());
ar.AsyncWaitHandle.WaitOne();//执行该方法时主线程将等待辅助线程执行完毕，使两线程同步后再执行以下语句
Console.WriteLine("其实很简单");//执行一些方法
string ret = andl.EndInvoke(out threadID, ar);//使用EndInvoke来获取返回结果和传入ref和out的变量获取修改后的实例的位置
Console.WriteLine("The call executed on thread {0},with return value : {1}",threadID, ret);
Console.ReadLine();
}
}
}
第三种方法是轮询BeginInvoke()返回的IAsyncResult的属性IsCompleted。
第四种方法是调用EndInvoke()时如果异步线程没有执行完，则会阻塞在这里。
==参考文献==