| |
为Windows应用创建简单的异步调用模式 |
|
时间: 2005-10-18 来自:msdn |
 |
|
一个简化的异步调用模式
我经常使用一项技术来减少创建异步调用时的复杂度和代码量,那就是把线程切换和委托的实现放入一个中间类中。这就使得我们从用户界面类中进行异步调用时,不用再去考虑什么线程和委托的问题。我把这项技术叫做自动回调。使用这项技术,上面的样例可以进行如下改进:
private void SomeUIEvent( object sender, EventArgs e ) { // Start retrieving the customer data. _serviceAgent.BeginGetCustomerData( "Joe Bloggs" ); } | 当Web服务访问完成后,以下的方法就会自动被调用:
private void GetCustomerDataCompleted( DataSet customerData ) { // This method will be called on the UI thread. // Update the user interface. _dataGrid.DataSource = customerData; } | 回调函数的名称由原始异步调用的名称来决定(因此就不再需要创建和传递委托了),并且可以保证被正确的线程所调用(也就不再需要使用Control.Invoke了),这些方法都很简单并且不容易出错。
天下没有免费的午餐,实现这个简单模型的神奇代码是需要我们来编写的。下面所列的就是被编写进ServiceAgent类中的这些代码:
public class ServiceAgent : AutoCallbackServiceAgent { private CustomerWebService _proxy;
// Declare a delegate to describe the autocallback // method signature. private delegate void GetCustomerDataCompletedCallback( DataSet customerData );
public ServiceAgent( object callbackTarget ) : base( callbackTarget ) { // Create the Web service proxy object. _proxy = new CustomerWebService(); }
public void BeginGetCustomerData( string customerId ) { _proxy.BeginGetCustomerData( customerId, new AsyncCallback( GetCustomersCallback ), null ); }
private void GetCustomerDataCallback( IAsyncResult ar ) { DataSet customerData = _proxy.EndGetCustomerData( ar );
InvokeAutoCallback( "GetCustomerDataCompleted",new object[] { customerData },typeof( GetCustomersCompletedCallback ) ); } } | 这个样例中服务代理类的代码是简单容易的,而且完全可以重用,我们所要做的就是给WinForm类编写一套类似的通用代码。我们有效的提升了线程管理工作的重要性,并且把它同编写后台异步调用对象代码的工作以及编写前台客户端代码的工作分离了开来。 AutoCallbackServiceAgent基类是一个实现了InvokeAutoCallback方法的简单类,代码如下:
public class AutoCallbackServiceAgent { private object _callbackTarget;
public AutoCallbackServiceAgent( object callbackTarget ) { // Store reference to the callback target object. _ callbackTarget = callbackTarget; }
protected void InvokeAutoCallback( string methodName,object[] parameters,Type delegateType ) { // Create a delegate of the correct type. Delegate autoCallback = Delegate.CreateDelegate( delegateType, _callbackTarget, methodName );
// If the target is a control, make sure we // invoke it on the correct thread. Control targetCtrl = _callbackTarget as System.Windows.Forms.Control; if ( targetCtrl != null && targetCtrl.InvokeRequired ) { // Invoke the method from the UI thread. targetCtrl.Invoke( autoCallback, parameters ); } else { // Invoke the method from this thread. autoCallback.DynamicInvoke( parameters ); } } } | 以上这些代码创建了一个回调函数的委托,并且判断是在调用线程,还是在用户界面线程中调用它。如果调用的目标是一个控件对象,那么它就会在需要的时候从用户界面线程来调用回调函数。
探究这些有趣的细节,如果你仔细的查看代码,你会发现我们可以通过不在基类中指定自动回调委托来进行简化。如果我们不需要对回调委托进行签名,我们就可以几乎自动化的处理所有的事情,把基础的服务代理类简化成只在BeginGetCustomerData方法中实现一行代码。
那我们为什么还要指定这个委托呢?那是因为我们还需要使用Control.Invoke方法。不幸的是.NET Framework的开发者并没有为这一方法提供一个MethodInfo对象,而恰恰是它可以使编写基础代码的工作变得简单许多。
一个替代的办法是指定一个标准的委托类型,把它用于所有的回调函数签名。举例来说,我们可以要求所有的自动回调函数都使用一个方法签名,这个方法签名用来维护原始的对象组,并且向客户端回传Web服务的参数。委托的声明方法如下:
| public delegate void AutoCallback( object[] parameters ); | 使用这个委托我们可以极大地简化服务代理类的代码,但是必须在客户端代码中把返回的数据转换成一定的格式。
这样做值得么?
有必要像上面一样实现一个服务代理类吗?这取决于你想多大程度上简化用户界面开发人员的工作。编写一个如上的服务代理类不一定会减少代码量,但可以使界面开发人员和后台服务开发人员的分工更加明确有效。
除了提供这种简单的异步调用模式外,我们还可以往服务代理类中添加更多的有用功能。以后我会继续在这个思路的基础上加以扩展,向你展示如何在服务代理类上实现诸如自动本地数据缓存等高级功能。在服务代理类上实现这些高级功能意味着用户界面开发人员可以更加轻松的完成工作了。
|
|
|
|
|
|
|
|