【问题标题】:Fail async method without throwing an exception在不引发异常的情况下使异步方法失败
【发布时间】:2020-07-12 19:33:12
【问题描述】:

假设我有以下方法,以及调用它的代码:

public async Task<MyResult> PerformAction(string parameter)
{
   if(parameter == "fail")
      throw new Exception("You wanted me to fail.");
   return await MyResult.Create(parameter);
}

var resultOne = await PerformAction("fail");
var resultTwo = await PerformAction("success");

这可以正常工作 - 但它会引发异常,从而导致异常的性能成本。

有没有办法向调用者指示任务失败,抛出异常?

【问题讨论】:

  • 您可以在MyResult 中包含bool IsSuccess 属性。
  • 您在PerformAction 上缺少async
  • 顺便说一句,例外是那些不应该指示操作结果(甚至是坏结果)但失败的东西。如果代码干净并正确实施,则应该尽可能少地抛出异常。在环境适合应用程序需求的完美场景中,不会抛出异常。 Exception 收集堆栈跟踪,这是一个缓慢而繁重的操作。
  • @GuruStron - 谢谢,我编辑了帖子。

标签: c# asynchronous exception async-await


【解决方案1】:

你可以这样做:

public Task<MyResult> PerformAction(string parameter)
{
   if(parameter == "fail")
   {
      return Task.FromException<MyResult>(new Exception("You wanted me to fail."));
   }
   return MyResult.Create(parameter);
}

不确定这在性能方面会快多少。

【讨论】:

  • 我认为Exception 的性能将是相同的,但您已经成功消除了对性能有积极影响的async 状态机。
  • 应该是FromException&lt;MyResult&gt;否则不会编译
  • @aepot 是的,我会假设相同,但尚未检查。
  • 另一个性能提示:ValueTask
  • @aepot ValueTasks 没有Exception 属性,因此要在没有状态机的情况下传播故障,无论如何您都必须包装Task。但是,如果异常的类型和消息始终相同,则可以缓存 Task
【解决方案2】:

尽可能避免异常生成和传播的常见解决方案是将结果包装在外部对象中:

public class Response<T> {
   public IEnumerable<string> Errors {get;set;}
   public bool HasErrors => Errors?.Any() ?? false;
   public bool IsSuccess {get;set;}
   public T Data {get;set;}
}

public async Task<Response<MyResult>> PerformAction(string parameter)
{
   if(parameter == "fail")
      return new Reponse<MyResult>() {
          IsSuccess = false,
          Errors = new string[] {"Failed"}
      };
   return new Response<MyResult>(){
             Result = await MyResult.Create(parameter).ConfigureAwait(false),
             IsSuccess = true
           };
}

var resultOne = await PerformAction("fail");
if (resultOne.HasErrors) { ... }
var resultTwo = await PerformAction("success");
if (resultTwo.HasErrors) { ... }

【讨论】:

  • null 不是有效结果时,我可以使用null 表示失败。但是,当null 是有效结果时,它不能用于指示失败。在那种情况下,我可能倾向于返回一个包装类型或一个元组。但是...我希望有一种“更清洁”的方式来做到这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-28
  • 1970-01-01
  • 2011-07-19
  • 1970-01-01
相关资源
最近更新 更多