【问题标题】:Is it possible for async method to ever return null in C#?异步方法是否有可能在 C# 中返回 null?
【发布时间】:2020-09-17 06:33:33
【问题描述】:

在挖掘一些旧代码时,我注意到一个对 asyncmethod 的调用,然后检查返回的任务是否为 null

async Task<Something> DoSomeStuffAsync()
{
    //...
    return null; //not the actual return, but I guess it doesn't matter
}


var result = DoSomeStuffAsync(); //without await
if(result == null)
{
    //does this part makes any sense
}

根据我对async 关键字的理解,这种情况永远不可能发生,因为async 方法的结果将始终包装在Task 中,但只是为了检查一下,我是否遗漏了什么?

在 C# 中 async 方法是否会返回 null

【问题讨论】:

  • 你测试过吗?如果是这样,您的测试结果是什么导致了这个问题?
  • 是的,这听起来很简单,编写单元测试就是手指训练——你完成了 90% 的工作。从字面上看,确实花了更多时间输入编写测试所需的这个问题。
  • 我测试过,它不返回null
  • 这个问题并不是专门针对我的情况,而是任何人都应该检查 null 异步方法吗?
  • async 不是合同的一部分(签名)。如果实现者更改了实现以直接管理返回的Task(删除了async 关键字)并以某种方式引入了null 结果,那么您认为它不能返回null 的假设是不正确的。不要假设实现将使用编译器生成的状态机。

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


【解决方案1】:

标记为async 的方法不可能返回null

但是,对于调用者,方法不是async,它只是返回一个可等待的(Task... 或其他可等待的类型,这超出了这个问题),所以它确实有可能返回null

即调用者无法区分:

async Task Foo()
//...

和:

Task Foo()
//...

而后一种方法可以完美返回null

所以空检查对于调用者来说是完全有效的

【讨论】:

  • 您是说作为async 方法的消费者的某人不知道该方法是否是异步的,但只会知道它返回一个任务?这是为什么呢?
  • @meJustAndrew 因为async 不是签名的一部分......只是一个语法糖(大而复杂,但本质上是编译器更改代码的语法糖)。编译后的IL代码对async一无所知
  • (实际上,这不是 100% 正确的......异步方法信息的元数据包含一些特定属性,因此 - 可能 - 知道方法是否为 async,但是让我们不要太深入)
  • 无论如何,深入探讨是没有意义的。单元测试不应该关心方法是如何实现的,只是它满足合同(重申async 不是其中的一部分)。您可以查找属性并基于此测试是否为 null,但随后您测试的是编译器,而不是方法。
【解决方案2】:

虽然DoSomeStuffAsync() 确实有朝一日可以更改为不是async 的方法,但很明显它的意图是async,您可能已经发现了一个错误。这个电话可能是为了等待。如果不打算等待呼叫(即,result 真的意味着是 Task),那么 null 检查是没有意义的,并且可能应该有一条注释来解释为什么不等待呼叫。

测试潜在的未来 API 更改(例如 DoSomeStuffAsync() 可能返回 null)不属于生产代码。不信任 API 合同和文档(方法名称中的“Async”是一种文档形式),并且使用无意义的 null 检查乱扔代码是没有经验的编码人员的特点。

【讨论】:

    【解决方案3】:

    According to MSDN article async 方法可以有以下返回类型:

    • 任务,用于返回值的异步方法。

    • 任务,用于执行操作但不返回值的异步方法。 void,用于事件处理程序。

    • 从 C# 7.0 开始,任何具有可访问 GetAwaiter 方法的类型。 GetAwaiter 方法返回的对象必须实现 System.Runtime.CompilerServices.ICriticalNotifyCompletion 界面。

    • 从 C# 8.0 开始,IAsyncEnumerable,用于返回异步流的异步方法。

    所以Task代表异步方法的执行,它不应该是nullbecause Task represents the ongoing process for the caller with a commitment to produce an actual value when the work is complete.

    【讨论】:

      猜你喜欢
      • 2013-05-03
      • 2018-01-24
      • 1970-01-01
      • 2010-09-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-04
      • 1970-01-01
      相关资源
      最近更新 更多