异步方法C#中的异常处理

本文关键字:异常处理 异步方法 | 更新日期: 2023-09-27 18:30:13

我知道这个问题已经被问了好几次了,但是,我看到的是一个稍微不同的变体。

public async Task<string> SomeAsyncMethod(string url)
{
     // do some URL validation
     if (!valid)
     {
          throw new Exception("some error");
     }
     // do async stuff now
     return await GetFromUrl(url)
}
// now in caller
public async Task<string> SomeOtherAsyncMethod(string input)
{
     var task = SomeAsyncMethod(input); 
    // there is potential chance that a validation fails and  
    //exception is thrown even before entering async parts of the called function
     // do some independent stuff here
    try
    {
        await task;
    } 
    catch(Exception e)
    {
        // log error 
    }
    // is the following code correct way to handle exceptions?
    if (!task.IsFaulted)
    {
        return task.Result; 
    }
    // log error from task.Exception 
    return null;
}

在上面的代码中,甚至在控件进入方法的异步部分之前,验证就可能失败并引发异常。我们是否需要将第一次通话也包含在尝试中。。抓块?我的实验表明这是没有用的。相反,任务状态设置为Faulted。因此,我认为检查Task状态并相应地返回数据是正确的。C#专业人士可以对此发表评论吗?

异步方法C#中的异常处理

正如您已经说过的,当您有一个抛出异常的async方法时,调用该方法永远不会抛出,相反,返回的任务只会出错。即使在第一个await之前抛出异常,也是如此。如果这是您想要的功能,那么您已经拥有了它,无需更改任何内容。

我们是否需要将第一次通话也包含在尝试中。。抓块?

您可能希望这样做,作为一种防御编码措施。async方法中的"前提条件"异常遇到的问题与枚举器块相同。在async的情况下,先决条件异常用于使任务出错,而不是直接引发。这就是我处理先决条件例外的方式。

然而,还有一种选择。实现可以"急切地"进行前提条件检查,并且只使用出现故障的任务来表示异步异常。即:

public Task<string> SomeMethodAsync(string url)
{
   // do some URL validation
   if (!valid)
   {
     throw new Exception("some error");
   }
   // do async stuff now
   return SomeMethodImplAsync(url);
}
private async Task<string> SomeMethodImplAsync(string url)
{
  return await GetFromUrl(url)
}

我自己不这么做,但这种方法确实有支持者。最引人注目的是Jon Skeet。

考虑到这一点,除非文档明确指定先决条件异常将放置在返回的任务上,否则您可能应该在try块中包含对SomeMethdAsync的调用。