Подавление "предупреждения CS4014: поскольку этот вызов не ожидается, выполнение текущего метода продолжается..." - программирование

Подавление "предупреждения CS4014: поскольку этот вызов не ожидается, выполнение текущего метода продолжается..."

Это не дубликат "Как безопасно вызывать метод async на С# без ожидания".

Как я могу подавить следующее предупреждение?

предупреждение CS4014: поскольку этот вызов не ожидается, выполнение текущего метода продолжается до завершения вызова. Рассмотрите возможность применения оператора "ожидание" к результату вызова.

Простой пример:

static async Task WorkAsync()
{
    await Task.Delay(1000);
    Console.WriteLine("Done!");
}

static async Task StartWorkAsync()
{
    WorkAsync(); // I want fire-and-forget 

    // more unrelated async/await stuff here, e.g.:
    // ...
    await Task.Delay(2000); 
}

То, что я пробовал и не любил:

static async Task StartWorkAsync()
{
    #pragma warning disable 4014
    WorkAsync(); // I want fire-and-forget here
    #pragma warning restore 4014
    // ...
}

static async Task StartWorkAsync()
{
    var ignoreMe = WorkAsync(); // I want fire-and-forget here
    // ...
}

Обновлено, поскольку исходный принятый ответ был отредактирован, я изменил принятый ответ на тот, который использует сброс С# 7.0, так как я не думаю, что ContinueWith уместен здесь. Всякий раз, когда мне нужно регистрировать исключения для операций "огонь-и-забыть", я использую более сложный подход, предложенный Стивеном Клири здесь.

4b9b3361

Ответ 1

С С# 7 теперь вы можете использовать отбрасывания:

_ = WorkAsync();

Ответ 2

Вы можете создать метод расширения, который предотвратит предупреждение. Метод расширения может быть пустым или вы можете добавить обработку исключений с помощью .ContinueWith().

static class TaskExtensions
{
    public static void Forget(this Task task)
    {
        task.ContinueWith(
            t => { WriteLog(t.Exception); },
            TaskContinuationOptions.OnlyOnFaulted);
    }
}

public async Task StartWorkAsync()
{
    this.WorkAsync().Forget();
}

Однако ASP.NET подсчитывает количество запущенных задач, поэтому он не будет работать с простым расширением Forget() как указано выше, и вместо этого может завершиться с ошибкой:

Асинхронный модуль или обработчик завершен, пока асинхронная операция еще не выполнена.

С.NET 4.5.2 его можно решить, используя HostingEnvironment.QueueBackgroundWorkItem:

public static Task HandleFault(this Task task, CancellationToken cancelToken)
{
    return task.ContinueWith(
        t => { WriteLog(t.Exception); },
        cancelToken,
        TaskContinuationOptions.OnlyOnFaulted,
        TaskScheduler.Default);
}

public async Task StartWorkAsync()
{
    System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(
        cancelToken => this.WorkAsync().HandleFault(cancelToken));
}

Ответ 3

Вы можете украсить метод следующим атрибутом:

[System.Diagnostics.CodeAnalysis.SuppressMessage("Await.Warning", "CS4014:Await.Warning")]
static async Task StartWorkAsync()
{
    WorkAsync();
    // ...
}

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

Важной частью этого кода является второй параметр. Часть "CS4014:" - это то, что подавляет предупреждение. Вы можете написать все, что захотите, в остальном.

Ответ 4

Мой два способа справиться с этим.

Просто подавить его

#pragma warning disable 4014 - достаточно хорошее решение для "стрелять и забывать".

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

Если у вас возникли проблемы с запоминанием того, как записать #pragma warning disable 4014, просто позвольте Visual Studio добавить его для вас. Нажмите Ctrl+. чтобы открыть "Быстрые действия", а затем "Подавить CS2014",

Сохраните его в локальной переменной

пример

var task = Task.Run(() => DoMyStuff()).ConfigureAwait(false);

Поскольку последующего вызова task, в режиме деблокирования локальная переменная должна быть немедленно оптимизирована, как и никогда. На самом деле, я сомневаюсь, что компилятор даже создаст локальную переменную (кто-то может подтвердить ее декомпилятором).

В общем

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

Ответ 5

Простым способом остановки предупреждения является просто назначение задачи при вызове:

Task fireAndForget = WorkAsync(); // No warning now

И так в вашем исходном посте вы бы сделали:

static async Task StartWorkAsync()
{
    // Fire and forget
    var fireAndForget = WorkAsync(); // Tell the compiler you know it a task that being returned 

    // more unrelated async/await stuff here, e.g.:
    // ...
    await Task.Delay(2000); 
}

Ответ 6

Причиной предупреждения является WorkAsync возвращает Task которая никогда не читается или не ожидается. Вы можете установить для возвращаемого типа WorkAsync значение void и предупреждение исчезнет.

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

static async void WorkAsync()
{
    await Task.Delay(1000);
    Console.WriteLine("Done!");
}

static async Task StartWorkAsync()
{
    WorkAsync(); // no warning since return type is void

    // more unrelated async/await stuff here, e.g.:
    // ...
    await Task.Delay(2000); 
}

Ответ 7

Почему бы не обернуть его внутри метода async, который возвращает void? Используется бит, но все переменные используются.

static async Task StartWorkAsync()
{   
     async void WorkAndForgetAsync() => await WorkAsync();
     WorkAndForgetAsync(); // no warning
}

Ответ 8

Сегодня я нашел этот подход случайно. Вы можете определить делегата и сначала назначить метод async для делегата.

    delegate Task IntermediateHandler();



    static async Task AsyncOperation()
    {
        await Task.Yield();
    }

и назовите его так

(new IntermediateHandler(AsyncOperation))();

...

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