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.