假设两个类,都是同一个超类的后代,如下所示:
class MySuperClass{}
class A : MySuperClass{}
class B : MySuperClass{}
然后这个赋值不会通过编译器:
MySuperClass p = myCondition ? new A() : new B();
编译器抱怨A和B不兼容(条件表达式的类型无法确定,因为'A'和'B'之间没有隐式转换 [CS0173])。但它们都是MySuperClass类型,所以在我看来这应该有效。不是说这是一个大问题;只需要一个简单的强制转换即可启发编译器。但肯定是C#编译器的一个障碍?你不同意吗?
答案 0 :(得分:10)
条件的结果应该是相同的类型。他们不是。
来自MSDN,(?:运营商):
first_expression和second_expression的类型必须相同,或者从一种类型到另一种类型必须存在隐式转换。
由于A
和B
不是同一类型,并且您似乎没有定义隐式转换,因此编译器会抱怨。
答案 1 :(得分:4)
查看这篇blog,了解一些有趣的文章,了解为什么C#编译器会做/不做“明显”的事情。该博客由 Eric Lippert 编写, C#编译器开发人员之一。
答案 2 :(得分: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中只有一个有a 类型,以及x和y 可以直接转换为那个 类型,然后就是那种类型 条件表达式。
·否则,无法确定表达式类型,并发生编译时错误。
基本上,操作数必须可以转换为彼此,而不能相互转换为其他类型。
这就是为什么你需要在你的例子中或在诸如nullables(int? foo = isBar ? 42 : (int?)null
)之类的情况下进行显式转换。声明类型不会影响评估,编译器必须从表达式本身中找出它。
答案 3 :(得分:3)
编译器不会尝试寻找共同的祖先,因此您需要一个显式的强制转换来显示您想要将其视为祖先的祖先;在你的情况下:
MySuperClass p = myCondition ? (MySuperClass)(new A()) : (MySuperClass)(new B());
这意味着条件运算符双方都返回相同的类型,这满足了编译器。
答案 4 :(得分:3)
条件运算符(与任何其他运算符一样)必须定义其表达式所代表的类型。在条件运算符的情况下,它有两个步骤:
没有祖先搜索,因为实施可能会导致你作为开发人员在该表达式中指定的模糊性的滑坡。一切都应该导致object
吗?那些值类型会被隐含地装箱?接口怎么样?如果两种类型之间有多个公共接口,应该选择哪一种?
在您的情况下,正如您所发现的,您需要将其中一个操作数向上转换为父类型。一旦你这样做,规则2.)就满足了(总是有一个隐式转换,从一个更具体的类型转变为一个不太具体的类型)。
请注意,您只需要将转换应用于其中一个操作数,而不是两者。
答案 5 :(得分:1)
Rowland Shaw总结得很好,但是为了明白为什么不会隐式使用共同的祖先,请考虑如果两个类都要实现特定的接口会发生什么。编译器无法确定哪个用于条件运算符的类型,因此它将被强制使用object
。