Следует ли выделить отдельное событие для асинхронного делегата? - PullRequest
1 голос
/ 16 февраля 2020

Для асинхронных делегатов в коде я везде делаю следующее:

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());
    }
}

Есть ли смысл в таком решении (отделить событие для asyn c) или это пример плохой дизайн? Если это плохо, то я хотел бы понять, почему и как лучше всего вызывать asyn c делегатов?

Ответы [ 2 ]

0 голосов
/ 17 февраля 2020

это пример плохого дизайна?

Это вероятно плохой дизайн. Синхронная версия вызывает делегаты в потоке пула потоков (вместо того, чтобы какой-либо поток вызвал событие), и это вызывает их как запуск и забывание, что означает, что любые исключения будут молча проглатываться и игнорироваться. Обычно это плохо.

Если это плохо, то я хотел бы понять, почему и как лучше всего вызывать asyn c делегатов?

Вам нужно вызывать асинхронно делегирует асинхронно - из асинхронного метода. Не всегда возможно безопасно вызвать асинхронный метод (или делегировать) синхронно, хотя есть некоторые хаки, которые работают в большинстве ситуаций .

0 голосов
/ 16 февраля 2020

Вызов события SomeChangedAsync не реализован правильно. В случае нескольких обработчиков событий ожидается только последний подключенный обработчик. Чтобы дождаться всех обработчиков, вы должны получить их с помощью метода GetInvocationList, а затем решить, как вы хотите их вызывать и ожидать. Вот последовательный подход:

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;
        }
    }
}

Вы также можете вызывать и ожидать их всех одновременно (используя Task.WhenAll), как предлагается здесь .

...