Search code examples
c#async-awaitextension-methods

how can I combine await.WhenAny() with GetAwaiter extension method


I want to await a button.click() event. For that created an extension GetAwaiter() method:

public static class ButtonAwaiterExtensions
{
    public static ButtonAwaiter GetAwaiter(this Button button)
    {
        return new ButtonAwaiter()
        {
            Button = button
        };
    }

    public class ButtonAwaiter : INotifyCompletion
    {
        public bool IsCompleted
        {
            get { return false; }
        }

        public void GetResult()
        {

        }

        public Button? Button { get; set; }

        public void OnCompleted(Action continuation)
        {
            RoutedEventHandler? h = null;
            h = (o, e) =>
            {
                Button!.Click -= h;
                continuation();
            };
            Button!.Click += h;
        }

    }
}

(I found it here: http://blog.roboblob.com/2014/10/23/awaiting-for-that-button-click/)

With that I can await the Button1.Click() event directly with await Button1; which is great.

BUT I couldn't figure out how to combine this awaitable with someting like await Task.WhenAny()

I tried

await Task.WhenAny(Button1, Button2);

It will tell me that it "cannot convert from Button to Task".

I thougt I found the solution here: Using `Task.WhenAll` with `INotifyCompletion` by just adding a method

public static async Task GetTask()
{
    await this;
}

to my ButtonAwaiterExtensions class, but the keyword this cannot be used in my static class.

I cannot figure out what to return in the method or generally how to await any Button.Click(). Any ideas?


Solution

  • Thanks to Sebastian Schumann comment (how can I combine await.WhenAny() with GetAwaiter extension method) I solved my problem by adding another extension method directly to my ButtonAwaiterExtensions class:

    public async static Task AsTask(this Button self) => await self;
    

    Complete solution:

    public static class ButtonAwaiterExtensions
    {
        public static ButtonAwaiter GetAwaiter(this Button button)
        {
            return new ButtonAwaiter()
            {
                Button = button
            };
        }
    
        public class ButtonAwaiter : INotifyCompletion
        {
            public bool IsCompleted
            {
                get { return false; }
            }
    
            public void GetResult()
            {
    
            }
    
            public Button? Button { get; set; }
    
            public void OnCompleted(Action continuation)
            {
                RoutedEventHandler? h = null;
                h = (o, e) =>
                {
                    Button!.Click -= h;
                    continuation();
                };
                Button!.Click += h;
            }
        }
       public async static Task AsTask(this Button self) => await self;
    }
    

    An alternative (little shorter) Implementation for GetAwaiter might be:

    public static TaskAwaiter GetAwaiter(this Button self)
    {
        ArgumentNullException.ThrowIfNull(self);
        TaskCompletionSource tcs = new();
        self.Click += OnClick;
        return tcs.Task.GetAwaiter();
    
        void OnClick(object sender, EventArgs args)
        {
            self.Click -= OnClick;
            tcs.SetResult();
        }
    }