I\'d like to have a generic reusable piece of code for wrapping EAP pattern as task, something similar to what Task.Factory.FromAsync does for BeginXXX/EndXXX APM pattern.>
It is possible with a helper class and a fluent-like syntax:
public static class TaskExt
{
public static EAPTask> FromEvent()
{
var tcs = new TaskCompletionSource();
var handler = new EventHandler((s, e) => tcs.TrySetResult(e));
return new EAPTask>(tcs, handler);
}
}
public sealed class EAPTask
where TEventHandler : class
{
private readonly TaskCompletionSource _completionSource;
private readonly TEventHandler _eventHandler;
public EAPTask(
TaskCompletionSource completionSource,
TEventHandler eventHandler)
{
_completionSource = completionSource;
_eventHandler = eventHandler;
}
public EAPTask WithHandlerConversion(
Converter converter)
where TOtherEventHandler : class
{
return new EAPTask(
_completionSource, converter(_eventHandler));
}
public async Task Start(
Action subscribe,
Action action,
Action unsubscribe,
CancellationToken cancellationToken)
{
subscribe(_eventHandler);
try
{
using(cancellationToken.Register(() => _completionSource.SetCanceled()))
{
action();
return await _completionSource.Task;
}
}
finally
{
unsubscribe(_eventHandler);
}
}
}
Now you have a WithHandlerConversion helper method, which can infer type parameter from converter argument, which means you need to write WebBrowserDocumentCompletedEventHandler only one time.
Usage:
await TaskExt
.FromEvent()
.WithHandlerConversion(handler => new WebBrowserDocumentCompletedEventHandler(handler))
.Start(
handler => this.webBrowser.DocumentCompleted += handler,
() => this.webBrowser.Navigate(@"about:blank"),
handler => this.webBrowser.DocumentCompleted -= handler,
CancellationToken.None);