我正在实现一个定义异步方法的接口-一个返回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(...)”来在后台线程上执行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)
对象中?
答案 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(...)
否则,您可能会发现自己处在没有堆栈跟踪的断点上。