Можно ли объявить метод async возвратом void, чтобы отключить предупреждение CS4014?

Visual Studio выдает предупреждение для этого кода ( "потому что этот вызов не ожидается, выполнение текущего метода продолжается до завершения вызова" ).

static void Main(string[] args)
{
    FireAndForget(); // <-- Warning CS4014
    // Do something else.
}

static async Task FireAndForget()
{
    // Do something (cannot throw).
}

Я понимаю, что не стоит ждать задачи в этом конкретном случае, потому что FireAndForget никогда не будет генерировать исключение.

Вместо того, чтобы отключать предупреждение с помощью прагмы, я рассматривал возможность изменения возвращаемого типа FireAndForget от Task до void. Это эффективно отключает компилятор.

static async void FireAndForget() // <-- Task changed to void
{
    // Do something (cannot throw).
}

Однако, согласно Stephen Cleary, следует избегать методов async void, поэтому я не совсем уверен, что делать.

Можно ли использовать метод "async void", если метод не предназначен для того, чтобы быть в первую очередь ожидаемым, и если исключение не будет выбрано?

Ответ 1

Чрезвычайно редко бывает реальная операция "огонь-и-забыть"; то есть операцию, в которой:

  • Никто не заботится о завершении.
  • Никто не заботится о завершении.
  • Никто не заботится, если это исключает.

В частности, с последним из них; большинство так называемых операций "огонь-и-забыть" на самом деле не срабатывают и не забываются, потому что необходимо предпринять какие-то действия, если это не удастся.

Тем не менее, есть несколько ситуаций, когда применим настоящий огонь и-забыть.

Я предпочитаю использовать async Task и избегать предупреждения компилятора, назначая задачу другой неиспользуемой переменной:

var _ = FireAndForget();

async Task методы более многоразовые и проверяемые, чем методы async void.

Тем не менее, я бы не выбрал подходящий вариант, если разработчик в моей команде просто использовал async void.

Ответ 2

Можно ли использовать метод async void, если метод не разработан быть в первую очередь ожидаемым, и если не будет исключено исключение?

Хотя это может быть "ОК" для этого, я бы по-прежнему рекомендовал вам сделать метод async Task. Несмотря на то, что вы на 100 процентов уверены, что этот метод не будет бросать, и его не ждет, вы никогда не знаете, как он может привыкнуть. Теперь вы полностью осознаете, каковы последствия использования async void, но если есть небольшой шанс, что кому-то, возможно, потребуется использовать это в будущем, тогда вам лучше дать хороший комментарий о том, почему этот Task не ожидается, а не идти с легким путем сделать это void.

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

Ответ 3

Один потенциальный проблемный момент заключается в том, что теперь невозможно будет определить, вызвал ли код исключение или нет. Поэтому, если у вас есть модульные тесты для их обнаружения, модульные тесты никогда не будут работать.

Классический пример с сайта MSDN:

private async void ThrowExceptionAsync()
{
  throw new InvalidOperationException();
}
public void AsyncVoidExceptions_CannotBeCaughtByCatch()
{
  try
  {
    ThrowExceptionAsync();
  }
  catch (Exception)
  {
    // The exception is never caught here!
    throw;
  }
}

http://haacked.com/archive/2014/11/11/async-void-methods/

https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

Вместо использования async void, если это конкретный случай края, как насчет использования утверждений pragma?

#pragma warning disable CS-4014
... your code here ...
#pragma warning restore CS-4014

Таким образом вы можете отключить статический шум.

НТН...