C#和Java';s三元算子(?)

C#和Java';s三元算子(?),java,c#,ternary-operator,conditional-operator,Java,C#,Ternary Operator,Conditional Operator,我是一个新手,我只是遇到了一个问题。在处理三元运算符(?:)时,C#和Java之间存在差异 在下面的代码段中,为什么第4行不起作用?编译器显示错误消息“int”和“string”之间没有隐式转换。第五行也不行。两个列表都是对象,不是吗 int two = 2; double six = 6.0; Write(two > six ? two : six); //param: double Write(two > six ? two : "6"); //param: not object

我是一个新手,我只是遇到了一个问题。在处理三元运算符(
?:
)时,C#和Java之间存在差异

在下面的代码段中,为什么第4行不起作用?编译器显示错误消息
“int”和“string”之间没有隐式转换
。第五行也不行。两个列表都是对象,不是吗

int two = 2;
double six = 6.0;
Write(two > six ? two : six); //param: double
Write(two > six ? two : "6"); //param: not object
Write(two > six ? new List<int>() : new List<string>()); //param: not object
inttwo=2;
双六=6.0;
写(二>六?二:六)//参数:双
写(两个>六个?两个:“6”)//参数:不是对象
写入(两个>六个?新列表():新列表())//参数:不是对象
但是,同样的代码也适用于Java:

int two = 2;
double six = 6.0;
System.out.println(two > six ? two : six); //param: double
System.out.println(two > six ? two : "6"); //param: Object
System.out.println(two > six ? new ArrayList<Integer>()
                   : new ArrayList<String>()); //param: Object
inttwo=2;
双六=6.0;
System.out.println(2>6?2:6)//参数:双
System.out.println(二>六?二:“六”)//参数:对象
System.out.println(两个>六个?新的ArrayList()
:new ArrayList())//参数:对象

C#中缺少什么语言功能?如果有的话,为什么不添加呢?

这很简单。字符串和int之间没有隐式转换。三值运算符需要最后两个操作数具有相同的类型

尝试:


在Java和C#(以及大多数其他语言)中,表达式的结果都有一个类型。在三元运算符的情况下,有两个可能的子表达式对结果求值,并且它们必须具有相同的类型。对于Java,可以通过自动装箱将
int
变量转换为
整数。现在,由于
整数
字符串
都继承自
对象
,因此可以通过简单的缩小转换将它们转换为相同的类型

另一方面,在C#中,
int
是一个原语,不存在到
string
或任何其他
对象的隐式转换。查看C#5语言规范第7.14节:条件运算符,我们可以看到以下内容:

  • 如果x的类型是x,y的类型是y,那么

    • 如果存在从X到Y的隐式转换(§6.1),但不存在从Y到X的隐式转换,则Y是 条件表达式

    • 如果存在从Y到X的隐式转换(§6.1),但不存在从X到Y的隐式转换,则X是 条件表达式

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

换句话说:它试图找出x和y是否可以相互转换为,如果不能,则会发生编译错误。在我们的例子中,
int
string
没有显式或隐式转换,因此它不会编译

将此与以下内容进行对比:

  • 如果第二个和第三个操作数具有相同的类型(可能是null类型),则这就是条件表达式的类型。(
  • 如果第二个和第三个操作数中的一个是基元类型T,而另一个的类型是对T应用装箱转换(§5.1.7)的结果,则条件表达式的类型是T.(
  • 如果第二个和第三个操作数中的一个为null类型,而另一个操作数的类型为引用类型,则条件表达式的类型为该引用类型。(
  • 否则,如果第二个和第三个操作数的类型可转换为数值类型(§5.1.8),则有几种情况:(
  • 否则,第二个和第三个操作数分别为S1和S2类型。设T1为将装箱转换应用于S1的结果类型,T2为将装箱转换应用于S2的结果类型。
    条件表达式的类型是将捕获转换(§5.1.10)应用于lub(T1,T2)(§15.12.2.7)的结果。(

而且,我们可以看到它试图找到一个共同的祖先,该祖先将作为使用
对象
登陆它的调用所使用的类型<代码>对象是一个可接受的参数,因此调用将起作用。

关于泛型部分:


给出的答案是好的;我想补充一点,C#的这个规则是一个更一般的设计准则的结果。当被要求从几个选项中的一个推断表达式的类型时,C#选择其中唯一的最佳选项。也就是说,如果你给C#一些选择,比如“长颈鹿,哺乳动物,动物”,那么它可能会选择最普通的——动物——或者它可能会选择最具体的——长颈鹿——这取决于环境。但它必须从实际给出的选项中选择一个。C#从不说“我的选择是在猫和狗之间,因此我会推断动物是最好的选择”。这不是一个选择,所以C#不能选择

在三元运算符C#的情况下,尝试选择更通用的int和string类型,但这两种类型都不是更通用的类型。C#决定不能推断任何类型,而不是选择一个一开始就不是选择的类型,比如object

我还注意到,这与C#的另一个设计原则是一致的:如果出现问题,请告诉开发人员。这种语言并没有说“我会猜出你的意思,如果可以的话就混过去”。这种语言说:“我想你在这里写了一些让人困惑的东西,我要告诉你。”

另外,我注意到C#不是从变量到赋值的推理,而是从另一个方向。C#并没有说“您正在分配一个对象变量,因此表达式必须可转换为对象,因此我将确保它是”。相反,C#说“这个表达式必须有一个类型,并且我必须能够推断该类型与object兼容”。由于表达式没有类型,因此会产生错误。

根据“t
Write(two > six ? two.ToString() : "6");
two > six ? new List<int>() : new List<string>()
two > six ? new ArrayList<Integer>() : new ArrayList<String>()
void test( List<?> list ) {
    System.out.println("foo");
}

void test( ArrayList<Integer> list ) { // note: can't use List<Integer> here
                                 // since both test() methods would clash after the erasure
    System.out.println("bar");
}

void test() {
    test( true ? new ArrayList<Object>() : new ArrayList<Object>() ); // foo
    test( true ? new ArrayList<Integer>() : new ArrayList<Object>() ); // foo 
    test( true ? new ArrayList<Integer>() : new ArrayList<Integer>() ); // bar
} // compiler automagically binds the correct generic QED