解决警告CS1998时如何处理异常:此异步方法缺少'await'运算符

时间:2019-04-05 19:55:03

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

我正在实现一个定义异步方法的接口-一个返回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来捕获它并将其包装在失败的Task.FromException<ValidationResult>(ex)对象中?

2 个答案:

答案 0 :(得分: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);
  }
}

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

如果您经常执行此操作,则可以考虑创建一个辅助方法。 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 { /* ... */ };
  });
}

答案 1 :(得分:0)

正如其中一项评论所指出的那样,编译器为您做一些工作,以确保异步(使用单词await)方法的异常以异步方式出现,并且进一步影响调试行为,从而影响调用堆栈被保留。如果方法合同返回了一个任务,我的建议是让编译器为您完成这项工作...

return await Task.FromResult(...)

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