A reusable pattern to convert event into task

后端 未结 5 793
陌清茗
陌清茗 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:33

    I have a (usage wise) much shorter Solution. I will show you the usage first and then give you the code that makes this happen (use it freely).
    usage eg:

    await button.EventAsync(nameof(button.Click));
    

    or:

    var specialEventArgs = await busniessObject.EventAsync(nameof(busniessObject.CustomerCreated));
    

    or for Events that need to be triggered in some way:

    var serviceResult = await service.EventAsync(()=> service.Start, nameof(service.Completed));
    

    the magic that makes this happen (beware it's C# 7.1 syntax but can easily be converted back to lower language versions by adding a few lines):

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace SpacemonsterIndustries.Core
    {
        public static class EventExtensions
        {
            /// 
            /// Extension Method that converts a typical EventArgs Event into an awaitable Task 
            /// 
            /// The type of the EventArgs (must inherit from EventArgs)
            /// the object that has the event
            /// optional Function that triggers the event
            /// the name of the event -> use nameof to be safe, e.g. nameof(button.Click) 
            /// an optional Cancellation Token
            /// 
            public static async Task EventAsync(this object objectWithEvent, Action trigger, string eventName, CancellationToken ct = default)
                where TEventArgs : EventArgs
            {
                var completionSource = new TaskCompletionSource(ct);
                var eventInfo = objectWithEvent.GetType().GetEvent(eventName);
                var delegateDef = new UniversalEventDelegate(Handler);
                var handlerAsDelegate = Delegate.CreateDelegate(eventInfo.EventHandlerType, delegateDef.Target, delegateDef.Method);
    
                eventInfo.AddEventHandler(objectWithEvent, handlerAsDelegate);
    
                trigger?.Invoke();
    
                var result = await completionSource.Task;
    
                eventInfo.RemoveEventHandler(objectWithEvent, handlerAsDelegate); 
    
                return result;
    
                void Handler(object sender, TEventArgs e) => completionSource.SetResult(e);
            }
    
            public static Task EventAsync(this object objectWithEvent, string eventName, CancellationToken ct = default) where TEventArgs : EventArgs
                => EventAsync(objectWithEvent, null, eventName, ct);
    
            private delegate void UniversalEventDelegate(object sender, TEventArgs e) where TEventArgs : EventArgs;
        }
    }
    

提交回复
热议问题