I've got a method implemented using the Event-based Asynchronous method pattern. I also want to offer a synchronous version of that method, but don't want to re-write it (because the method involves a call to WCF from Silverlight, the Async version has to be the primary method).
I've come up with the following generic method to convert an Event-based Asynchronous call to a Synchronous one:
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;
}
So if I have this asynchronous method:
void ConnectAsync(string address,
string userName,
string password,
EventHandler<ConnectCompletedEventArgs> completionCallback)
I can convert it into a synchronous call like this:
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;
}
}
My concern is about the use of the eventArgs variable which gets captured in the closure. It's being set in one thread, and accessed from another. Is my use of the ManualResetEvent sufficient to guarantee a correct read of the value once the event has been signalled, or do I need to do something else?
Well you're at it, you might want to comment about the handling of Exceptions here. My plan is that the Async method will wrap an exceptions that occur lower down the stack in a ConnectionException, or something like that, so my thinking is that rethrowing the Exception is correct in this case.