【问题标题】:How do I handle Exceptions when resolving warning CS1998: This async method lacks 'await' operators解决警告 CS1998 时如何处理异常:此异步方法缺少“等待”运算符
【发布时间】:2019-08-27 18:40:29
【问题描述】:

我正在实现一个定义异步方法的接口 - 返回一个 Task<T> 对象的接口。

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

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

在大多数实现此接口的类中,Validate 方法中都有异步工作要做。因此,asyncawait 关键字被利用。

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(...)' 在后台线程上执行 CPU 密集型工作。

我从one particular question and answer 了解到,我可以在不包含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.FromException&lt;ValidationResult&gt;(ex) 将其包装在失败的Task 对象中会更好吗?

【问题讨论】:

  • 我让它成为异常气泡,让调用者决定他想做什么。
  • 一般来说应该返回一个失败的Task对象。如果一个方法返回一个Task,那么通常期望在尝试观察Task的结果时会在延迟庄园(IE)中抛出异常。这不适用于例如参数检查(null、超出范围等)

标签: c# .net asynchronous async-await task


【解决方案1】:

如果我的同步代码抛出异常,我是否应该让它原封不动地传递给调用者?或者,使用 Task.FromException(ex) 将其捕获并包装在失败的 Task 对象中会更好吗?

你应该把它放在返回的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);
  }
}

或者,您可以使用不带awaitasync 并使用#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 { /* ... */ };
}

如果你经常这样做,你可以考虑创建一个辅助方法。 This one 是 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 { /* ... */ };
  });
}

【讨论】:

    【解决方案2】:

    正如其中一位 cmets 指出的那样,编译器会为您做一些工作,以确保异步(使用单词 await)方法的异常以异步方式出现,并进一步影响调用堆栈如何影响调试行为被保留。如果方法契约返回一个任务,我的建议是让编译器为你完成这项工作......那就是

    return await Task.FromResult(...)
    

    否则,您可能会发现自己处于没有堆栈跟踪的断点上。

    【讨论】:

      猜你喜欢
      • 2012-10-25
      • 1970-01-01
      • 2019-11-21
      • 1970-01-01
      • 2017-04-30
      • 2014-02-13
      • 1970-01-01
      • 2019-09-25
      • 1970-01-01
      相关资源
      最近更新 更多