0

Intro

In my code there is a lot of places where I need to wait until an event fires before proceeding. Tasks (combined with await) are ideal for this, so I thought I'd make an extension method for EventHandler<T> that would simplify this kind of situation.

I need something like this pseudocode:

event EventHandler<int> AllFinished;

async Task<int> DoThings()
{
    InitiateAnAction();
    var n = await /* wait for AllFinished to fire */;
    return n;
}
    

Try #1

My first idea was that I'd implement an explicit cast from EventHandler to Task but I found out that operators cannot be overloaded with extension methods. If it had worked, it would have looked like this:

var n = await (Task)AllFinished;

.

Try #2

My second attempt was to use an ordinary extension method. This is the code I used:

public static Task<(object sender, T args)> Once<T>(this EventHandler<T> e)
{
    var tcs = new TaskCompletionSource<(object, T)>();
    EventHandler<T> d = null;

    d = delegate (object sender, T args)
    {
        tcs.SetResult( (sender, args) );
        e -= d;
    };

    e += d;
    return tcs.Task;
}

However I found out that events can't be passed as arguments, so the e += d and e -= d didn't do anything (they didn't even throw an error). If it had worked, it would have been used like this:

var (sender, number) = await AllFinished.Once();

.

Question

Is there an elegant way of awaiting events? I'd prefer an one-liner as in my two previous attempts:

var n = await (Task)AllFinished;
var (sender, number) = await AllFinished.Once();

If it's possible, please avoid passing event's name as a string. This approach would make it impossible to use the IntelliSense rename feature, which could break my code when I forget about it.

.

Why do I need this?

If you think that waiting for events inside methods is a result of a bad design, you can dive into my original Cef-related question and post a better solution there.

Community
  • 1
  • 1
m93a
  • 8,866
  • 9
  • 40
  • 58
  • If answers in duplicate question does not satisfy you - you may consider looking at Rx (reactive extensions). They have Observable.FromEvent and that observable can be awaited just like Task. – Evk Jun 26 '17 at 18:36
  • Rx allows you to *process* event streams using LINQ operators as if they were data. It's *very* useful for UIs, eg. to debounce clicks, respond to specific series of events, throttling. It's a lot more than just awaiting an event – Panagiotis Kanavos Jun 27 '17 at 07:37

0 Answers0