代理与异步编程 (by zguosir/gshzheng)
除了调用FCL中提供的异步方法外,.Net提供了一个非常有用的机制,使得任何一个方法都可以异步的调用,换言之,任何一个方法,都可以在单独的一个线程中异步的执行,执行完毕后将结果返回到调用线程。
这个机制就是使用代理(delegate)。代理是C#中引入的相当聪明的语言元素。从现象上看,代理象C++中的类型安全函数指针,所以常用于事件定义或其它回掉机制。本质上,代理是个特殊的类,它封装了函数调用的功能。尤其是代理的异步调用功能,使程序开发的灵活性大大增加,也大量地屏蔽了多线程开发的难度。
对任何方法,定义一个与其签名相同的代理,就可以通过代理的BeginInvoke(),EndInvoke()来异步的调用。下面是一个演示了异步调用阶乘计算的例子。
Example5
public class UseDelegate
{
//要调用的方法
public static int Factorial(int n)
{
if(n<=1) return 1;
return n*Factorial(n-1);
}
public delegate int DelegateFactorial(int
n);
//异步调用
public static void AsyncCall(int n)
{
DelegateFactorial
dele=new DelegateFactorial(Factorial);
dele.BeginInvoke(n,new AsyncCallback(cb_AsyncCall),null);
}
private static void cb_AsyncCall(IAsyncResult ar)
{
DelegateFactorial
d=((ar as AsyncResult).AsyncDelegate) as DelegateFactorial;
int i=d.EndInvoke(ar);
Console.WriteLine(i);
}
}
代理的BeginInvoke()的返回值是个AsyncResult对象,它实现了IAsyncResult接口,并且也记录了调用者代理实例dele,可以在回调函数中将ar强行转化为AsyncResult并取得AsyncDelegate后,通过它来调用EndInvoke()。注,其它的异步调用BeginOperation返回的对象不一定是AsyncResult类型,也未必有AsyncDelegate属性,需要借助于AsyncState参数等方式传入。
代理本质上是个类,继承自MulticastDelegate。MulticastDelegate 是一个特殊类。编译器和其他工具可以从此类派生,但是开发人员不能显式地从此类进行派生。 当定义了一个代理,
public delegate int DelegateFactorial(int
n);
编译器将会自动生成一个等价的类
public class DelegateFactorial
: System.MulticastDelegate
{
public DelegateFactorial(Object target, Int32
methodPtr);
public virtual void
Invoke(Int32 n);
public virtual
IAsyncResult BeginInvoke(Int32 n,AsyncCallback callback, Object state);
public virtual int EndInvoke(IAsyncResult result);
}
其中,构造方法在实例化一个代理对象时由CLR调用,并自动传入当前对象到第一个参数(如果是静态方法则传入null);Invoke()方法就是直接调用时的同步方法,目前的vs.net编译器不允许直接调用此方法;BeginInvoke(),EndInvoke()方法就是一对异步方法,它们的方法签名的格式与代理定义相一致:
l BeginInvoke()的后面固定为指定回调函数的两个参数;
l 代理定义的所有参数(包括ref,out参数),将作为BeginInvoke前面的参数;
l EndInvoke()的最后一个参数,固定为IAsyncResult;
l 如果代理的参数列表中有ref,out参数,将作为EndInvoke前面的参数;
l EndInvoke()的返回值即为代理的返回值。