Cancellable “awaiting” on .NET events continued

Compose, compose, compose. Reuse, reuse, reuse.

In the last post on cancellable “awaiting” on .NET events, I was so busy focusing on the wrapping the click event in order to get a smooth async control flow for the caller, I neglected to do the same inside the click event wrapper code.

The framework team already provided an extension method (WithCancellation) to make any task cancellable. By using WithCancellation(), we can do away with the Continuation call and just use a simple try/finally block to guarantee that the event handler gets unsubscribed regardless of how the task finishes.

 

    1: public static async Task<RoutedEventArgs> WhenClicked(this Button element, CancellationToken token)
    2: {
    3:     TaskCompletionSource<RoutedEventArgs> tsc = new TaskCompletionSource<RoutedEventArgs>();
    4:     RoutedEventHandler routedEventHandler = null;
    5:  
    6:     // set the event handler to complete the task
    7:     routedEventHandler = (Object s, RoutedEventArgs e) => tsc.SetResult(e);
    8:  
    9:     // hook up to the event
   10:     element.Click += routedEventHandler;
   11:     try
   12:     { 
   13:         return await tsc.Task.WithCancellation(token);
   14:     }
   15:     finally
   16:     {
   17:         // always unhook the event handler after the task is finished
   18:         element.Click -= routedEventHandler;
   19:     }
   20: }

FOR BONUS POINTS: Why do we have to make this method async? Why not just return a task instead and not use await on line 13.