1

For asynchronous delegates in the code, I do the following everywhere:

public class SomeCaller
{
    public event Action SomeChanged;
    public event Func<Task> SomeChangedAsync;

    //If in Caller async method
    public async Task SomeMethodAsync()
    {
        SomeChanged?.Invoke();
        if (SomeChangedAsync != null)
            await SomeChangedAsync();
    }

    //if in Caller synchronous method
    public void SomeMethod()
    {
        SomeChanged?.Invoke();
        if (SomeChangedAsync != null)
            Task.Run(async () => await SomeChangedAsync());
    }
}

Is there any point in such a solution (to separate the event for async) or is this an example of poor design? If this is bad, then I would like to understand why and how best to call async delegates?

2 Answers2

0

The invocation of the SomeChangedAsync event is not implemented correctly. In case of multiple event handlers, only the last handler attached will be awaited. To await all the handlers you must get them with the method GetInvocationList, and then decide how you want to invoke and await them. Here is a sequential approach:

public async Task SomeMethodAsync()
{
    SomeChanged?.Invoke();

    Delegate[] delegates = SomeChangedAsync?.GetInvocationList();
    if (delegates != null)
    {
        var taskFactories = delegates.Cast<Func<Task>>().ToArray();
        foreach (var taskFactory in taskFactories)
        {
            var task = taskFactory();
            await task;
        }
    }
}

You can also invoke and await them all at once (using Task.WhenAll), as it's suggested here.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
0

is this an example of poor design?

This is probably a poor design. The synchronous version raises the delegates on a thread pool thread (instead of whatever thread caused the event), and it raises them as fire-and-forget, which means any exceptions will be silently swallowed and ignored. That's usually bad.

If this is bad, then I would like to understand why and how best to call async delegates?

You need to call asynchronous delegates asynchronously - from an asynchronous method. It's not always possible to safely call an asynchronous method (or delegate) synchronously, though there are some hacks that work in most situations.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810