1
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();
4

4 回答 4

7

编译器首先尝试计算右手表达式:

? new Foo() : new Bar();

这两者之间没有隐式转换,因此出现错误消息。你可以这样做:

IFooBarable ting = a == 1 ? (IFooBarable)(new Foo()) : (IFooBarable)(new Bar());
于 2010-01-20T17:01:29.880 回答
5

这在 C# 语言规范的第 7.13 节中有介绍。基本上杀死这种情况的是,三元操作数的 2 个值的类型之间必须存在隐式转换。在没有变量类型的情况下考虑这种转换。

所以要么Foo必须可转换为Bar,反之亦然。也不是所以会发生编译错误。

后两者虽然有效,因为它们只考虑一种类型(或者FooBar)。因为它们是相同的类型,所以确定表达式的类型很简单并且可以正常工作。

于 2010-01-20T17:01:37.130 回答
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 表达式,它确实从其上下文中获取其类型。使该功能正常工作非常复杂。如果您有兴趣,请参阅我关于 lambda 表达式与匿名方法的博客系列。

第二个要素是我们从不为您“创造”一种类型。当给定一堆我们必须从中推断出类型的事物时,我们总是推断出实际上就在我们面前的类型。

在您的示例中,分析如下:

  • 找出后果的类型
  • 找出替代方案的类型
  • 找到与结果和替代都兼容的最佳类型
  • 确保从条件表达式的类型转换为使用条件表达式的事物的类型。

与第一点一致,我们不从外到内推理;我们不使用我们知道变量类型的事实来计算表达式的类型。但现在有趣的是,当你拥有

b ? new Cat() : new Dog()

我们说“条件表达式的类型是集合 {Cat, Dog} 中最好的类型”。我们不会说“条件表达式的类型是兼容 Cat 和 Dog 的最佳类型”。那将是哺乳动物,但我们不这样做。相反,我们说“结果必须是我们实际看到的”,在这两个选择中,都不是明显的赢家。如果你说

b ? (Animal) (new Cat()) : new Dog()

那么我们可以在 Animal 和 Dog 之间做出选择,而 Animal 显然是赢家。

现在,请注意,在进行这种类型分析时,我们实际上并没有正确实现 C# 规范!有关错误的详细信息,请参阅我的文章

于 2010-01-20T19:24:46.790 回答
1

因为条件表达式的类型总是从它的两部分推断出来,而不是从要应用结果的变量中推断出来。此推断仅在类型相等或一个与另一个引用兼容时才有效。在这种情况下,这两种类型中的任何一种都不能与另一种引用兼容。

于 2010-01-20T17:02:39.733 回答