A reusable pattern to convert event into task

后端 未结 5 798
陌清茗
陌清茗 2020-11-27 06:58

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.

5条回答
  •  爱一瞬间的悲伤
    2020-11-27 07:31

    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);
    

提交回复
热议问题