while(true)的独特重载分辨率

当遇到这种奇怪的情况时,我正在实现同步/asynchronous重载:

当我有一个没有参数或返回值的常规lambdaexpression式时,它会通过Action参数进入Run重载,这是可预测的。 但是,当lambda有一段while (true)在它使用Func参数进入重载。

 public void Test() { Run(() => { var name = "bar"; }); Run(() => { while (true) ; }); } void Run(Action action) { Console.WriteLine("action"); } void Run(Func<Task> func) // Same behavior with Func<T> of any type. { Console.WriteLine("func"); } 

输出:

行动
FUNC

那么,怎么可能呢? 是有原因的吗?

所以首先,第一个expression式只能调用第一个重载。 它不是一个Func<Task>的有效expression式,因为有一个返回无效值( void而不是Task )的代码path。

() => while(true)实际上是签名的有效方法。 (它与诸如() => throw new Expression();是有效的返回任何可能的types的方法体,包括void ,一个有趣的小问题,以及为什么自动生成的方法通常只是抛出一个exception;无论方法的签名如何,它都会被编译)无限循环的方法是没有代码path不返回正确值的方法(无论“正确值”是否为voidTask ,或从字面上的其他任何东西)。 这当然是因为它永远不会返回一个值,而且它是以编译器可以certificate的方式进行的。 (如果这样做是因为编译器无法certificate,毕竟它还没有解决停止问题,那么我们就和A 。)

所以,对于我们的无限循环来说,这更好,因为这两个超载都是适用的。 这带来了我们的C#规范的更好的部分。

如果我们继续阅读第7.4.3.3节,第4项,我们看到:

如果E是匿名函数,则T1和T2是具有相同参数列表的委托types或expression式树型,并且在该参数列表(第7.4.2.11节)的上下文中存在针对E的推断返回typesX:

[…]

如果T1有返回typesY,T2返回无效,那么C1是更好的转换。

所以当从一个匿名委托转换,这就是我们正在做的,它会喜欢转换返回一个值是void ,所以它selectFunc<Task>