只是在此处发布的正确答案中添加一点:有两个设计指南导致了此规范。
首先是我们“由内而外”的推理。当你说
double x = 2 + y;
我们先算出 x 的类型,然后算出 2 的类型,然后算出 y 的类型,然后算出 (2+y) 的类型,最后算出 x 和 (2+y) 的类型是否兼容。但是我们不使用 x 的类型来决定 2、y 或 2+y 的类型。
这是一个很好的规则的原因是因为“接收器”的类型通常正是我们想要解决的问题:
void M(Foo f) {}
void M(Bar b) {}
...
M(x ? y : z);
我们在这里做什么?我们必须计算出条件表达式的类型才能进行重载决策,以确定这将是 Foo 还是 Bar。因此,在我们分析条件表达式的类型时,我们不能使用这就是,比如说,去 Foo 的事实!这是一个先有鸡还是先有蛋的问题。
此规则的例外是 lambda 表达式,它确实从其上下文中获取其类型。使该功能正常工作非常复杂。如果您有兴趣,请参阅我关于 lambda 表达式与匿名方法的博客系列。
第二个要素是我们从不为您“创造”一种类型。当给定一堆我们必须从中推断出类型的事物时,我们总是推断出实际上就在我们面前的类型。
在您的示例中,分析如下:
- 找出后果的类型
- 找出替代方案的类型
- 找到与结果和替代都兼容的最佳类型
- 确保从条件表达式的类型转换为使用条件表达式的事物的类型。
与第一点一致,我们不从外到内推理;我们不使用我们知道变量类型的事实来计算表达式的类型。但现在有趣的是,当你拥有
b ? new Cat() : new Dog()
我们说“条件表达式的类型是集合 {Cat, Dog} 中最好的类型”。我们不会说“条件表达式的类型是兼容 Cat 和 Dog 的最佳类型”。那将是哺乳动物,但我们不这样做。相反,我们说“结果必须是我们实际看到的”,在这两个选择中,都不是明显的赢家。如果你说
b ? (Animal) (new Cat()) : new Dog()
那么我们可以在 Animal 和 Dog 之间做出选择,而 Animal 显然是赢家。
现在,请注意,在进行这种类型分析时,我们实际上并没有正确实现 C# 规范!有关错误的详细信息,请参阅我的文章。