Reputation: 11274
The base C# EventHandler
is defined as:
namespace System
{
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
}
Does anyone if there is an awaitable event handler available? E.g.
public delegate Task EventHandlerAsnyc<TEventArgs>(object sender, TEventArgs e);
Thx
Upvotes: 3
Views: 3164
Reputation: 5146
The downside of just using async void
handlers is that there is no way for the caller to wait for the result. This may be an issue for some interactive event handlers, like the ones using CancelEventArgs
.
But you can still declare a Task
-returning delegate type, if you wish. You just have to be careful how you raise it then. For instance, you could make an extension method which you can call as handler.Raise(sender, EventArgs.Empty)
.
public delegate Task EventHandlerAsnyc<TEventArgs>(object sender, TEventArgs eventArgs);
public static async Task Raise<TEventArgs>(this EventHandlerAsnyc<TEventArgs> handlers, object sender, TEventArgs eventArgs)
{
if (handlers == null)
return;
foreach (var handler in handlers.GetInvocationList())
await ((EventHandlerAsnyc<TEventArgs>)handler).Invoke(sender, eventArgs);
}
Alternatively, you can allow handlers to execute concurrently. But this would probably be a bad idea unless well-documented, as it's probably considered surprising behavior.
public static Task RaiseAllowConcurrent<TEventArgs>(this EventHandlerAsnyc<TEventArgs> handlers, object sender, TEventArgs eventArgs)
{
if (handlers == null)
return Task.CompletedTask;
var invocationList = handlers.GetInvocationList();
var tasks = new Task[invocationList.Length];
for (var i = 0; i < invocationList.Length; ++i)
tasks[i] = ((EventHandlerAsnyc<TEventArgs>)invocationList[i]).Invoke(sender, eventArgs);
return Task.WhenAll(tasks);
}
Upvotes: 3
Reputation: 43896
If you want your event to be processed async
(meaning you can use await
to return early and resume later) you can simply declare the handler as async void
:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponents();
myButton.Click += myButton_Click;
}
public async void myButton_Click(object sender, EventArgs e)
{
myButton.Enabled = false;
await SomeAsyncOrLongRunningOnAnotherThreadTask();
myButton.Enabled = true;
}
}
This way SomeAsyncOrLongRunningOnAnotherThreadTask()
won't block your UI thread. And the handler is resumed after that task completes.
Side note: normally async
methods should always return a Task
or Task<T>
that can be await
ed or otherwise handled by the caller. The use case above is (afaik) the only justified case where void
should be used for an async
method.
Upvotes: 7