条件运算符会混淆,但为什么呢?

假设有两个类,都是同一个超类的后代,如下所示:

class MySuperClass{} class A : MySuperClass{} class B : MySuperClass{} 

然后这个赋值不会通过编译器:

 MySuperClass p = myCondition ? new A() : new B(); 

编译器抱怨A和B不兼容( 无法确定条件表达式的类型,因为’A’和’B’之间没有隐式转换 [CS0173])。 但它们都是MySuperClass类型,所以在我看来这应该有效。 这不是一件大事; 只需要一个简单的强制转换即可启发编译器。 但肯定这是C#编译器的一个障碍? 你不同意吗?

条件的结果应该是相同的类型。 他们不是。

从MSDN ,( ?:运营商):

first_expression和second_expression的类型必须相同,或者从一种类型到另一种类型必须存在隐式转换。

由于AB不是同一类型,并且您似乎没有定义隐式转换,因此编译器会抱怨。

查看这篇博客,了解一些有趣的文章,了解为什么C#编译器会做/不做“明显”的事情。 该博客由C#编译器开发人员之一Eric Lippert编写。

请参阅语言规范的第7.14节

?:运算符的第二个和第三个操作数x和y控制条件表达式的类型。

·如果x的类型为X,则y的类型为Y.

o如果从X到Y存在隐式转换(第6.1节),而不是从Y到X,则Y是条件表达式的类型。

o如果从Y到X存在隐式转换(第6.1节),而不是从X到Y,则X是条件表达式的类型。

o否则,不能确定表达式类型,并发生编译时错误。

·如果x和y中只有一个具有类型,并且x和y都可以隐式转换为该类型,那么这就是条件表达式的类型。

·否则,不能确定表达式类型,并发生编译时错误。

从本质上讲,操作数必须可以相互转换,而不能相互转换为其他类型。

这就是为什么你需要在你的例子中或在诸如nullables( int? foo = isBar ? 42 : (int?)null )的情况下进行显式转换。 声明类型不会影响评估,编译器必须从表达式本身中找出它。

编译器不会尝试寻找共同的祖先,因此您需要一个显式的强制转换来显示您想要将其视为祖先的祖先; 在你的情况下:

 MySuperClass p = myCondition ? (MySuperClass)(new A()) : (MySuperClass)(new B()); 

这意味着条件运算符的双方都返回相同的类型,这满足编译器。

条件运算符(与任何其他运算符一样)必须定义其表达式所代表的类型。 在条件运算符的情况下,它有两个步骤:

  1. 是同一类型的操作数? 如果是这样,那就是表达式的类型。
  2. 是否存在从一个操作数类型到另一个操作数类型的隐式转换(但不是在两个方向上)? 如果是这样,那么“其他”就是表达式的类型。

没有祖先搜索,因为实现可以导致你作为开发人员可以在该表达式中指定的模糊性的滑坡。 一切都应该导致object ? 那些值类型会被隐含地装箱? 接口怎么样? 如果两种类型之间有多个公共接口,应该选择哪一种?

在您的情况下,正如您所发现的,您需要将其中一个操作数向上转换为父类型。 一旦你这样做,规则2.)就满足了(总是存在从更具体的类型到更不具体的类型的隐式转换)。

请注意,您只需要将转换应用于其中一个操作数,而不是两者。

Rowland Shaw总结得很好,但是为了看看为什么不会隐式使用共同的祖先,考虑如果两个类都是实现特定接口会发生什么。 编译器将无法确定哪个用于条件运算符的类型,因此它将被强制使用object