为什么异步等待抛出NullReferenceException?

Ani*_*ish 6 .net c# asynchronous visual-studio async-await

我的代码看起来像这样

var userStartTask = LroMdmApiService.AddUser(user);
 // .... do some stuff
await userStartTask;
Run Code Online (Sandbox Code Playgroud)

AddUser()抛出一个异常,它冒泡的NullReferenceException.它不等待等待.

但是,如果我像这样构造代码......

var result = await LroMdmApiService.AddUser(user);
Run Code Online (Sandbox Code Playgroud)

然后正常捕获异常.谁能告诉我这里发生了什么?

以下是显示问题的完整代码.这种情况的最佳做法是什么?

class Program
{
    private static void Main(string[] args)
    {
        CallAsync();
        Console.ReadKey();
    }

    public static async void CallAsync()
    {
        var task = CallExceptionAsync();
        ThrowException("Outside");
        await task;
    }

    public static Task CallExceptionAsync()
    {
        return Task.Run(() =>
        {
            ThrowException("Inside");
        });

    }

    public static void ThrowException(string msg)
    {
        throw new Exception(msg);
    }        
}
Run Code Online (Sandbox Code Playgroud)

Ste*_*ary 7

这段代码

var result = await LroMdmApiService.AddUser(user);
Run Code Online (Sandbox Code Playgroud)

与此代码几乎完全相同:

var task = LroMdmApiService.AddUser(user);
var result = await task;
Run Code Online (Sandbox Code Playgroud)

当AddUser()抛出异常时,它会冒泡为NullReferenceException.它不等待等待.

AddUser大概是这样的(这里_servicenull):

public static Task AddUser(User user)
{
  return _service.AddUser(user);
}
Run Code Online (Sandbox Code Playgroud)

这将导致a 直接NullReferenceException抛出,而不是放在返回的任务上.

如果您总是希望将异常放在返回的任务上,那么请使用每个返回任务的方法async:

public static async Task AddUser(User user)
{
  return await _service.AddUser(user);
}
Run Code Online (Sandbox Code Playgroud)

但是,您应该考虑是否真的要这样做.NullReferenceException特别是代码错误; 这不是你应该在生产中捕捉或关心的例外.要使用Eric Lippert的术语,这是一个愚蠢的例外.

在我看来,抛出骨头异常的位置并不重要 - 无论是直接抛出还是放在任务上 - 因为这些异常只针对开发人员,而不是运行时.