这种将异步方法转换为同步方法的方法正确吗?

4
我已经使用基于事件的异步方法模式实现了一个方法。我还想提供该方法的同步版本,但不想重新编写它(因为该方法涉及从Silverlight调用WCF,异步版本必须是主要方法)。
我想出了以下通用方法,将基于事件的异步调用转换为同步调用:
 Func<TArg1, TArg2, TArg3, TEventArgs> 
    CreateSynchronousMethodFromAsync<TArg1, TArg2, TArg3, TEventArgs>(
       Action<TArg1, TArg2, TArg3, EventHandler<TEventArgs>> asyncMethod) 
         where TEventArgs : AsyncCompletedEventArgs
    {
        Func<TArg1, TArg2, TArg3, TEventArgs> syncMethod = (arg1, arg2, arg3) =>
         {
             TEventArgs eventArgs = null;

             using (var waitHandle = new ManualResetEvent(false))
             {
                 asyncMethod(arg1, arg2, arg3, (sender, e) =>
                                          {
                                              eventArgs = e;
                                              waitHandle.Set();
                                          });

                 waitHandle.WaitOne();

                 return eventArgs;
             }
         };

        return syncMethod;
    }

那么如果我有这个异步方法:

void ConnectAsync(string address, 
     string userName, 
     string password, 
     EventHandler<ConnectCompletedEventArgs> completionCallback)

我可以将它转换为同步调用,像这样:
public void Connect(string address, string userName, string password)
{
    Func<string, string, string, ConnectCompletedEventArgs> connect = 
        CreateSynchronousMethodFromAsync<string, string, string, ConnectCompletedEventArgs>(ConnectAsync);

    var connectResult = connect(address, userName, password);

    if (connectResult.Error != null)
    {
        throw connectResult.Error;
    }
}

我的关注点是eventArgs变量的使用,它在闭包中被捕获。它在一个线程中被设置,并从另一个线程中访问。我的ManualResetEvent的使用是否足以保证一旦事件被发出,正确读取该值,还是需要做其他事情?
顺便说一下,您可能希望评论此处异常的处理方式。我的计划是Async方法将在堆栈较低处发生的异常包装在ConnectionException或类似的异常中,因此我认为在这种情况下重新抛出异常是正确的。
1个回答

2

基于你所提供的ASync模式,这看起来是一个很好的尝试来封装异步调用。

然而,让我犹豫的是方法名称 'BeginConnect';一些.NET类有 'BeginXxx' / 'EndXxx' 成对处理异步调用,这些通常指定正确操作需要从回调处理程序中调用 'EndXxx' 调用,而这是你的方案没有考虑到的。

如果你正在封装的调用确实符合链接页面上讨论的模式,那么这应该可以工作。但如果你要封装的调用是第二种类型的调用,那就不太行了...


我的代码仅适用于符合基于事件的异步方法模式的方法 - 我的内部方法BeginConnect负责封装Begin/End调用,但我没有展示其实现。 - Samuel Jack
请注意,您可能希望将'throw connectionResult.Error'包装为新异常的内部异常,以便保留原始异常的堆栈跟踪,而不是直接抛出。 - jerryjvl
2
@Sam:我建议根据链接文章中的约定命名该方法,以避免混淆...将其改为“ConnectAsync”。 - jerryjvl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接