Как обрабатывать исключения при разрешении предупреждения CS1998: в этом асинхронном методе отсутствуют операторы ожидания - PullRequest
2 голосов
/ 05 апреля 2019

Я реализую интерфейс, который определяет асинхронный метод - тот, который возвращает Task<T> объект.

public class ValidationResult
{
    // Properties that will hold the results of a validation.
}

public interface IValidator
{
    Task<ValidationResult> Validate(object objectToValidate);
}

В большинстве классов, реализующих этот интерфейс, в методе Validate выполняется асинхронная работа. Таким образом, ключевые слова async и await используются.

public class ExampleAsyncValidator : IValidator
{
    public override async Task<ValidationResult> Validate(object objectToValidate)
    {
        // Perform some asynchronous calls here which will use the await keyword.

        return new ValidationResult { /* ... */ };
    }
}

Однако некоторые классы, реализующие этот интерфейс , не выполняют асинхронную работу в методе Validate.

public class ExampleSyncValidator : IValidator
{
    public override async Task<ValidationResult> Validate(object objectToValidate)
    {
        // Perform only synchronous calls here. No use of the await keyword.

        return new ValidationResult { /* ... */ };
    }
}

Если ключевое слово async используется для метода Validate в синхронном сценарии выше, то я получаю предупреждение CS1998 от компилятора.

В этом асинхронном методе отсутствуют операторы ожидания и он будет работать синхронно. Подумайте об использовании оператора «await» для ожидания неблокирующих вызовов API или «await Task.Run (...)» для выполнения работы с привязкой к ЦП в фоновом потоке.

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

public class ExampleSyncValidator : IValidator
{
    public override Task<ValidationResult> Validate(object objectToValidate)
    {
        // Perform only synchronous calls here. No use of the await keyword.

        return Task.FromResult(new ValidationResult { /* ... */ });
    }
}

Мой вопрос: Какова наилучшая практика обработки исключений в этом сценарии?

Если мой синхронный код выдает Исключение, должен ли я позволить ему остаться нетронутым? Или было бы лучше поймать его и обернуть в неудачный объект Task, используя Task.FromException<ValidationResult>(ex)?

Ответы [ 2 ]

1 голос
/ 08 апреля 2019

Если мой синхронный код выдает исключение, должен ли я позволить ему остаться нетронутым? Или было бы лучше перехватить его и обернуть в невыполненный объект Task с помощью Task.FromException (ex)?

Вы должны разместить его на возвращенном Task; то есть используйте Task.FromException.

public override Task<ValidationResult> Validate(object objectToValidate)
{
  try
  {
    // Perform only synchronous calls here. No use of the await keyword.
    return Task.FromResult(new ValidationResult { /* ... */ });
  }
  catch (Exception ex)
  {
    return Task.FromException<ValidationResult>(ex);
  }
}

Также вы можете использовать async без await и подавить предупреждение с помощью #pragma:

#pragma warning disable 1998
public override async Task<ValidationResult> Validate(object objectToValidate)
#pragma warning restore 1998
{
  // Perform only synchronous calls here. No use of the await keyword.
  return new ValidationResult { /* ... */ };
}

Если вы делаете это много, вы можете посмотреть на создание вспомогательного метода. Этот является частью Nito.AsyncEx:

public static class TaskHelper
{
#pragma warning disable 1998
  public static async Task ExecuteAsTask(Action func)
#pragma warning restore 1998
  {
    func();
  }

#pragma warning disable 1998
  public static async Task<T> ExecuteAsTask<T>(Func<T> func)
#pragma warning restore 1998
  {
    return func();
  }
}

Таким образом, если вы устанавливаете Nito.AsyncEx или включаете приведенный выше код в свой проект, то вы можете использовать метод ExecuteAsTask как таковой:

using static Nito.AsyncEx.TaskHelper;
...
public override Task<ValidationResult> Validate(object objectToValidate)
{
  return ExecuteAsTask(() => {
    // Perform only synchronous calls here. No use of the await keyword.
    return new ValidationResult { /* ... */ };
  });
}
0 голосов
/ 06 апреля 2019

Как указывает один из комментариев, компилятор делает некоторую работу для вас, чтобы убедиться, что исключения из асинхронных (использующих слово await) методов выводятся асинхронно, и, кроме того, на поведение отладки влияет то, как стек вызовов сохраняется Если контракт метода возвращает задачу, я рекомендую, чтобы компилятор выполнил эту работу за вас ... то есть

return await Task.FromResult(...)

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

...