basically, i have a method which invokes an event handler. the event handler calls an async method and i need to know the results of that method (true or false). since event
You could declare the Success
property as Task<bool>
instead of bool
. Then assign it inside the handler like this:
private void SomeEventHandler(object sender, MyEventArgs e)
{
e.Success = AnAsyncMethod(); // Without await
}
At the end you would be able to get the result after invoking the event and awaiting the property.
public virtual async Task<bool> TrySomething()
{
var args = new MyEventArgs();
SomeEvent?.Invoke(this, args);
return await (args.Success ?? Task.FromResult(false));
}
The whole reason async void
is allowed is for event handlers. From the documentation on Async Return Types:
You use the void return type in asynchronous event handlers, which require a void return type.
That said, events are designed to "signal the occurrence of an action" to any interested party, if any (and there can be none, or multiple). Think of it like sending an email notification to whomever subscribed to a specific mailing list: those people might have to do something when they receive the notification, but it's not your concern - you move on to your next task after sending the email. That's all events are. An event handler should not be something that is important for the proper functioning of the object.
Events are not designed to be a "hey this happened, what should I do next?" So there should be no need to await an event handler.
If returning args.Success
depends on SomeEventHandler
completing successfully, then it shouldn't be an event handler. Instead, you can have a Func<Task<bool>>
property (a function that returns Task<bool>
). Something like this:
public class SomeClass {
private Func<Task<bool>> IsSuccessful;
public SomeClass(Func<Task<bool>> isSuccessful) {
// Accept a reference to a function and store it
IsSuccessful = isSuccessful;
}
public async Task<bool> DoSomething() {
// Call our function and return the result
return await IsSuccessful();
}
}
Then you could use it like this:
// This is the method we want it to call
private async Task<bool> AnAsyncMethod() {
await Task.Delay(1);
return true;
}
// so we pass it in the constructor of the class.
// You don't have to pass it in the constructor - this is just an example
var myClass = new SomeClass(AnAsyncMethod);
In that way, it's very clear that SomeClass
cannot function properly without calling that method and therefore exactly one implementation of that method must be passed into the class.
is there any guarantee that the method TrySomething will wait for the SomeEvent to finish so Success has been set, before returning it?
No. async void
means "don't notify the caller when I finish". So the code raising your event cannot know when the event handler has completed, unless you write that additional logic yourself.
And if not, how can I ensure that it does?
Well, that's more of a tricky question. .NET events are designed as what I call "notification events" - that is, when an event fires, it notifies all its listeners. There's no need for "waiting" because there's no way for a listener to provide feedback to the notifier.
Your code is an example of what I call a "command event" - code that is an event
but doesn't match the semantics of a notification event. Your code wants a response from the handler.
So, the first question you need to ask yourself is "do I really want this to be an event?" One good litmus test for this is "can I define meaningful semantics if there are multiple handlers?"
More specifically, how should your code behave if multiple handlers are hooked up to the event? Perhaps the answer is "that wouldn't make sense". Or perhaps the answer is "I want to wait for all of them to complete and only be 'successful' if all of them are 'successful'". Or "wait for all and be 'successful' if any of them are 'successful'". Or "wait for the first one to complete and use that result". Or "wait for them to complete one at a time, stopping on the first success". Or "wait for them to complete one at a time, stopping on the first failure". These are the choices that come immediately to mind; there could be more.
If the answer is "that's not going to happen in my code" or "more than one handler doesn't make sense" or "that's too hard of a decision to make right now", then the appropriate answer is to remove the event
. It's not an event. It's a method call. In Design Pattern terminology, events
are used to implement the Observer Pattern, but what you have is a Strategy Pattern, and that's why event
s are a poor fit. In this case, you could use Gabriel's answer or something similar where you define the Strategy using an interface, and instead of raising an event you would invoke a method on that interface.
However, if it does make sense to have multiple handlers, and there is a meaningful semantic you can use, then you'll need to modify the EventArgs
type to have some sort of "collector" of responses, and then have your event-raising code interpret those responses. The exact code would vary based on the semantics you need.