C# · 12月 30, 2021

c# – 将事件转换为任务的可重用模式

我想要一个通用的可重用的代码片段为 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);