我已经使用基于事件的异步方法模式实现了一个方法。我还想提供该方法的同步版本,但不想重新编写它(因为该方法涉及从Silverlight调用WCF,异步版本必须是主要方法)。
我想出了以下通用方法,将基于事件的异步调用转换为同步调用:
我可以将它转换为同步调用,像这样:
我的关注点是eventArgs变量的使用,它在闭包中被捕获。它在一个线程中被设置,并从另一个线程中访问。我的ManualResetEvent的使用是否足以保证一旦事件被发出,正确读取该值,还是需要做其他事情?
顺便说一下,您可能希望评论此处异常的处理方式。我的计划是Async方法将在堆栈较低处发生的异常包装在ConnectionException或类似的异常中,因此我认为在这种情况下重新抛出异常是正确的。
我想出了以下通用方法,将基于事件的异步调用转换为同步调用:
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或类似的异常中,因此我认为在这种情况下重新抛出异常是正确的。