我想要一个通用的可重用的代码片段为
wrapping EAP pattern as task,类似于
Task.Factory.FromAsync
为
BeginXXX/EndXXX
APM pattern.
例如.:
private async void Form1_Load(object sender,EventArgs e) { await TaskExt.FromEvent<EventArgs>( handler => this.webBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(handler),() => this.webBrowser.Navigate("about:blank"),handler => this.webBrowser.DocumentCompleted -= new WebBrowserDocumentCompletedEventHandler(handler),CancellationToken.None); this.webBrowser.Document.InvokeScript("setTimeout",new[] { "document.body.style.backgroundColor = 'yellow'","1" }); }
到目前为止,它看起来像这样:
public static class TaskExt { public static async Task<TEventArgs> FromEvent<TEventArgs>( Action<EventHandler<TEventArgs>> registerEvent,Action action,Action<EventHandler<TEventArgs>> unregisterEvent,CancellationToken token) { var tcs = new TaskCompletionSource<TEventArgs>(); EventHandler<TEventArgs> handler = (sender,args) => tcs.TrySetResult(args); registerEvent(handler); try { using (token.Register(() => tcs.SetCanceled())) { action(); return await tcs.Task; } } finally { unregisterEvent(handler); } } }
是否可以提出类似的东西,但是不需要使用两次WebBrowserDocumentCompletedEventHandler(对于registerEvent / unregisterEvent),而不需要反思?
解决方法
可以使用助手类和流畅的语法:
public static class TaskExt { public static EAPTask<TEventArgs,EventHandler<TEventArgs>> FromEvent<TEventArgs>() { var tcs = new TaskCompletionSource<TEventArgs>(); var handler = new EventHandler<TEventArgs>((s,e) => tcs.TrySetResult(e)); return new EAPTask<TEventArgs,EventHandler<TEventArgs>>(tcs,handler); } } public sealed class EAPTask<TEventArgs,TEventHandler> where TEventHandler : class { private readonly TaskCompletionSource<TEventArgs> _completionSource; private readonly TEventHandler _eventHandler; public EAPTask( TaskCompletionSource<TEventArgs> completionSource,TEventHandler eventHandler) { _completionSource = completionSource; _eventHandler = eventHandler; } public EAPTask<TEventArgs,TOtherEventHandler> WithHandlerConversion<TOtherEventHandler>( Converter<TEventHandler,TOtherEventHandler> converter) where TOtherEventHandler : class { return new EAPTask<TEventArgs,TOtherEventHandler>( _completionSource,converter(_eventHandler)); } public async Task<TEventArgs> Start( Action<TEventHandler> subscribe,Action<TEventHandler> unsubscribe,CancellationToken cancellationToken) { subscribe(_eventHandler); try { using(cancellationToken.Register(() => _completionSource.SetCanceled())) { action(); return await _completionSource.Task; } } finally { unsubscribe(_eventHandler); } } }
现在你有一个WithHandlerConversion帮助程序,它可以推断转换器参数的type参数,这意味着你只需要一次编写WebBrowserDocumentCompletedEventHandler.
用法:
await TaskExt .FromEvent<WebBrowserDocumentCompletedEventArgs>() .WithHandlerConversion(handler => new WebBrowserDocumentCompletedEventHandler(handler)) .Start( handler => this.webBrowser.DocumentCompleted += handler,() => this.webBrowser.Navigate(@"about:blank"),handler => this.webBrowser.DocumentCompleted -= handler,CancellationToken.None);