一尘不染

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

c#

我想有一个通用的可重用的代码包装EAP模式的任务,类似什么东西Task.Factory.FromAsync了呢BeginXXX/EndXXXAPM模式

例如:

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),而不是诉诸反射?


阅读 259

收藏
2020-05-19

共1个答案

一尘不染

可以使用帮助器类和流利的语法:

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 action,
        Action<TEventHandler> unsubscribe,
        CancellationToken cancellationToken)
    {
        subscribe(_eventHandler);
        try
        {
            using(cancellationToken.Register(() => _completionSource.SetCanceled()))
            {
                action();
                return await _completionSource.Task;
            }
        }
        finally
        {
            unsubscribe(_eventHandler);
        }
    }
}

现在,您有了一个WithHandlerConversion辅助方法,该方法可以从转换器参数中推断类型参数,这意味着您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);
2020-05-19