Исключить исключение из асинхронной лямбда

Я пытаюсь написать метод, который пытается выполнить действие, но проглатывает все возникающие исключения.

Моя первая попытка следующая:

public static void SafeExecute(Action actionThatMayThrowException) {
    try {
        actionThatMayThrowException();
    } catch {
        // noop
    }
}

Что работает при вызове с синхронным действием:

SafeExecute(() => { 
    throw new Exception(); 
});

Однако сбой при вызове с асинхронным действием:

SafeExecute(async () => { 
    await Task.FromResult(0);
    throw new Exception(); 
});

Можно ли написать метод, который обрабатывает оба сценария?

Ответ 1

Чтобы правильно обрабатывать делегатов async, вы не должны использовать Action (это приведет к тому, что лямбда-выражение будет async void, что является опасным, и should избегайте), вы должны использовать Func<Task>, чтобы иметь возможность await:

public static async Task SafeExecute(Func<Task> asyncActionThatMayThrowException)
{
    try
    {
        await asyncActionThatMayThrowException();
    }
    catch
    {
        // noop
    }
}

Это решит случай async, но не синхронный случай. Вы не можете делать оба с помощью одного метода. Для этого вам понадобится другой метод, но он все равно может вызвать async один для включения повторного использования:

private static readonly Task<object> _completedTask = Task.FromResult<object>(null);

public static void SafeExecute(Action actionThatMayThrowException)
{
    SafeExecute(() =>
    {
        actionThatMayThrowException();
        return _completedTask;
    });
}

На самом деле я бы не рекомендовал игнорировать необработанные исключения таким образом. Вы должны рассмотреть хотя бы регистрацию исключения.