条件运算符感到困惑,但为什么呢? [重复]

问题描述 投票:0回答:6

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

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

那么这个赋值就不会通过编译器:

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

编译器抱怨 A 和 B 不兼容(无法确定条件表达式的类型,因为“A”和“B”之间没有隐式转换[CS0173])。但它们都是 MySuperClass 类型,所以在我看来这应该可行。并不是说这有什么大不了的。一个简单的转换就足以启发编译器。但这肯定是 C# 编译器中的一个障碍吗?你不同意吗?

c# inheritance
6个回答
10
投票

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

来自 MSDN,(?:操作员):

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

由于

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


4
投票

请参阅语言规范第 7.14 节

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

· 如果 x 具有类型 X 并且 y 具有 然后输入 Y

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

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

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

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

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

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

这就是为什么您需要在示例中或在可空值等情况下进行显式转换 (

int? foo = isBar ? 42 : (int?)null

)。声明类型不会影响计算,编译器必须从表达式本身中找出它。



3
投票
编译器不会尝试寻找共同的祖先,因此您需要显式转换来显示您想要将其视为哪个祖先;对于你的情况:

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

这意味着条件运算符两边返回相同的类型,这满足了编译器的要求。


3
投票
条件运算符(与任何其他运算符一样)必须定义其表达式表示的类型。对于条件运算符,它有一个两步过程:

    操作数的类型相同吗?如果是这样,那就是表达式的类型。
  1. 是否存在从一种操作数类型到另一种操作数类型的隐式转换(但不是双向)?如果是这样,那么“其他”就是表达式的类型。
没有祖先搜索,因为实现这一点可能会导致您作为开发人员在该表达式中指定的内容陷入歧义。一切都应该导致

object

吗?那么值类型又会被隐式装箱吗?接口怎么样?如果两种类型之间有多个公共接口,应该选择哪一种?

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

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


1
投票
Rowland Shaw 总结得很好,但要了解为什么不隐式使用共同祖先,请考虑如果两个类也实现特定接口会发生什么。编译器将无法确定使用哪个条件运算符的类型,因此将被迫使用

object

 来代替。

© www.soinside.com 2019 - 2024. All rights reserved.