这是 C# 的两个特性融合的结果。
首先,C# 永远不会为您“魔法”出一种类型。如果 C# 必须从给定的一组类型中确定“最佳”类型,它总是选择您提供的类型之一。它从来不会说“你给我的类型都不是最好的类型;因为你给我的选择都很糟糕,所以我会随机选择一些你没有给我选择的东西。”
第二个是 C# 的原因是inside to outside。我们不会说“哦,我看到您正在尝试将条件运算符结果分配给 ILogger;让我确保两个分支都能工作。”相反的情况会发生:C# 说“让我确定两个分支返回的最佳类型,并验证最佳类型是否可转换为目标类型。”
第二条规则是明智的,因为目标类型可能是我们想要确定的。当你说D d = b ? c : a;
目标类型是什么一目了然。但假设你正在打电话M(b?c:a)
? M 可能有一百种不同的重载,每种重载都有不同的形式参数类型!我们必须确定参数的类型是什么,然后丢弃由于参数类型与形参类型不兼容而不适用的 M 重载;我们不会走相反的路。
考虑一下如果我们走另一条路会发生什么:
M1( b1 ? M2( b3 ? M4( ) : M5 ( ) ) : M6 ( b7 ? M8() : M9() ) );
假设 M1、M2 和 M6 各有一百个重载。你做什么工作?你说,好吧,如果这是 M1(Foo) 那么 M2(...) 和 M6(...) 必须都可以转换为 Foo。他们是吗?让我们来看看吧。 M2的过载是多少?有一百种可能性。让我们看看它们中的每一个是否都可以从 M4 和 M5 的返回类型进行转换...好吧,我们已经尝试了所有这些,所以我们找到了一个可以工作的 M2。那么M6呢?如果我们找到的“最好的”M2 与“最好的”M6 不兼容怎么办?我们是否应该回溯并继续重新尝试所有 100 x 100 种可能性,直到找到兼容的一对?问题变得越来越严重。
We do以这种方式对 lambda 进行推理,因此涉及 lambda 的重载决策在 C# 中至少是 NP-HARD。那里的情况很糟糕;我们宁愿不添加更多的 NP-HARD 问题供编译器解决。
您也可以在语言的其他地方看到第一条规则的作用。例如,如果你说:ILogger[] loggers = new[] { consoleLogger, suppressLogger };
你会得到类似的错误;推断的数组元素类型必须是最佳类型给出的键入表达式。如果无法从中确定最佳类型,我们不会尝试寻找您未提供的类型。
类型推断也是如此。如果你说:
void M<T>(T t1, T t2) { ... }
...
M(consoleLogger, suppressLogger);
那么T就不会被推断为ILogger;这将是一个错误。 T 被推断为提供的参数类型中的最佳类型,并且其中没有最佳类型。
有关此设计决策如何影响条件运算符行为的更多详细信息,请参阅我关于该主题的系列文章.
如果您对为什么“从外到内”工作的重载解析是 NP-HARD 感兴趣,请参阅本文.