当我遇到这种特殊情况时,我正在实现同步/异步重载:
当我有一个没有参数或返回值的常规 lambda 表达式时,它会转到带有 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");
}
输出:
action
func
那怎么可能呢?有什么理由吗?
最佳答案
所以首先,第一个表达式只能调用第一个重载。它不是 Func<Task>
的有效表达式,因为存在返回无效值的代码路径(void
而不是 Task
)。
() => while(true)
实际上是任一签名的有效方法。 (它与 () => throw new Expression();
等实现一起是返回任何可能类型的有效方法体,包括 void
,这是一个有趣的琐事点,以及为什么从 IDE 自动生成的方法通常只抛出异常;无论方法的签名。)无限循环的方法是其中没有不返回正确值的代码路径的方法(无论“正确值”是 void
、 Task
还是其他任何字面意思都是如此)。这当然是因为它从不 返回一个值,而且它以编译器可以证明的方式返回。 (如果它以编译器无法证明的方式这样做,因为它毕竟没有解决停止问题,那么我们将与 A
在同一条船上。)
因此,对于我们的无限循环,考虑到两个重载都适用,哪个更好。这将我们带到了 C# 规范的优势部分。
如果我们转到第 7.4.3.3 节的第 4 点,我们会看到:
If E is an anonymous function, T1 and T2 are delegate types or expression tree types with identical parameter lists, and an inferred return type X exists for E in the context of that parameter list (§7.4.2.11):
[...]
if T1 has a return type Y, and T2 is void returning, then C1 is the better conversion.
因此,当从匿名委托(delegate)转换时,这就是我们正在做的事情,它会更喜欢返回值的转换,而不是 void
,因此它选择 Func<Task>
。
关于c# - 带有 while (true) 的特殊重载决议,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36137960/