public class Foo : IFooBarable {...}
public class Bar : IFooBarable {...}
那么为什么这不会编译...
int a = 1;
IFooBarable ting = a == 1 ? new Foo() : new Bar();
但这会......
IFooBarable ting = a == 1 ? new Foo() : new Foo();
IFooBarable ting = a == 1 ? new Bar() : new Bar();
答案 0 :(得分:7)
编译器首先尝试评估右手表达式:
? new Foo() : new Bar();
这两者之间没有隐式转换因此错误消息。你可以这样做:
IFooBarable ting = a == 1 ? (IFooBarable)(new Foo()) : (IFooBarable)(new Bar());
答案 1 :(得分:5)
这将在C#语言规范的第7.13节中介绍。基本上杀死这种情况的是,三元操作数的2个值的类型之间必须存在隐式转换。这种转换被认为是变量类型的绝对值。
因此,Foo
必须可转换为Bar
,反之亦然。也不会发生编译错误。
后者2可以工作,因为他们只考虑1种类型(Foo
或Bar
)。因为它们属于同一类型,所以确定表达式的类型很简单,并且工作正常。
答案 2 :(得分:4)
只需在此处发布的正确答案中添加一些内容:有两条设计指南可用于此规范。
首先,我们从“从内到外”推理。当你说
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表达式,做从其上下文中获取其类型。使该功能正常工作非常复杂;如果您有兴趣,请参阅我的blog series关于lambda表达式与匿名方法。
第二个要素是我们永远不会为你“魔术化”一种类型。如果给出了一些我们必须推断出类型的东西,我们总是推断出一种实际上正好在我们面前的类型。
在您的示例中,分析如下:
与第一点保持一致,我们没有推理从外到内;我们不使用这样一个事实,即我们知道我们要去的变量的类型,以便计算出表达式的类型。但现在有趣的是,当你有
时b ? new Cat() : new Dog()
我们说“条件表达式的类型是集{Cat,Dog}中的最佳类型”。我们不会说“条件表达式的类型是与Cat和Dog兼容的最佳类型”。那将是哺乳动物,但我们不这样做。相反,我们说“结果必须是我们实际看到的东西”,而在这两个选择中,两者都不是明显的赢家。如果你说
b ? (Animal) (new Cat()) : new Dog()
然后我们可以选择Animal和Dog,而Animal则是明显的赢家。
现在,请注意我们在进行此类型分析时实际上没有正确实现C#规范!有关错误的详细信息,请参阅我的article。
答案 3 :(得分:1)
因为条件表达式的类型总是从它的两个部分推断出来,而不是从要应用结果的变量推断出来。此推理仅在类型相等或一个引用与另一个相容时才有效。在这种情况下,这两种类型都不能与另一种类型的参考兼容。