C#和Java的三元运算符之间的区别(?:)

时间:2016-02-05 18:15:41

标签: java c# ternary-operator conditional-operator

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

在以下代码段中,为什么第4行不起作用?编译器显示there is no implicit conversion between 'int' and 'string'的错误消息。第5行不起作用。 List都是对象,不是吗?

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

但是,相同的代码适用于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

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

5 个答案:

答案 0 :(得分:105)

通过 C#5语言规范部分7.14:条件运算符,我们可以看到以下内容:

  
      
  • 如果x的类型为X且y的类型为Y,则

         
        
    • 如果从X到Y存在隐式转换(第6.1节),而不是从Y到X,那么Y是   条件表达。

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

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

    •   
  •   

换句话说:它试图找出x和y是否可以转换为 eachother ,如果没有,则会发生编译错误。在我们的情况下,intstring没有显式或隐式转换,因此无法编译。

将此与Java 7 Language Specification section 15.25: Conditional Operator

进行对比
  
      
  • 如果第二个和第三个操作数具有相同的类型(可能是空类型),那么这就是条件表达式的类型。 (
  •   
  • 如果第二个和第三个操作数之一是原始类型T,而另一个操作数的类型是将装箱转换(第5.1.7节)应用于T的结果,则条件表达式的类型为T.(
  •   
  • 如果第二个和第三个操作数之一是null类型而另一个操作数的类型是引用类型,则条件表达式的类型是该引用类型。 (
  •   
  • 否则,如果第二个和第三个操作数的类型可转换(第5.1.8节)为数字类型,则有几种情况:(
  •   
  • 否则,第二和第三操作数分别为S1和S2类型。设T1是将拳击转换应用到S1时产生的类型,让T2为应用到S2的装箱转换产生的类型。
      条件表达式的类型是将捕获转换(第5.1.10节)应用于lub(T1,T2)(第15.12.2.7节)的结果。 (
  •   

并且,查看section 15.12.2.7. Inferring Type Arguments Based on Actual Arguments我们可以看到它尝试找到一个共同的祖先,它将作为用于调用Object的调用的类型。 Object 可接受的参数,因此调用可以正常工作。

答案 1 :(得分:85)

给出的答案是好的;我想补充一点,这个C#规则是更通用的设计指南的结果。当被要求从几个选项之一推断出表达式的类型时, C#选择其中最独特的。也就是说,如果你给C#一些选择,比如&#34; Giraffe,Mammal,Animal&#34;然后它可能会选择最一般的 - 动物 - 或者它可能会选择最具体的 - 长颈鹿 - 视情况而定。但它必须选择实际给出的一个选择。 C#从未说过#34;我的选择是在Cat和Dog之间,因此我会推断出Animal是最好的选择&#34;。这不是一个选择,所以C#不能选择它。

在三元运算符的情况下,C#尝试选择更通用的int和string类型,但两者都不是更通用的类型。 C#决定不推断任何类型,而不是首先选择一个不是首选的类型,而不是选择类型。

我还注意到这符合C#的另一个设计原则:如果出现问题,请告诉开发人员。语言没有说&#34;我会猜测你的意思,如果我可以&#34;那就糊里糊涂。语言说&#34;我认为你在这里写了一些令人困惑的东西,我会告诉你这个。&#34;

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

答案 2 :(得分:23)

关于泛型部分:

two > six ? new List<int>() : new List<string>()

在C#中,编译器尝试右侧表达式部分转换为某种常见类型;由于List<int>List<string>是两种不同的构造类型,因此无法将其转换为另一种类型。

在Java中,编译器尝试查找公共超类型而不是转换,因此代码的编译涉及隐式使用wildcardstype erasure;

two > six ? new ArrayList<Integer>() : new ArrayList<String>()

的编译类型为ArrayList<?>(实际上,它也可以是ArrayList<? extends Serializable>ArrayList<? extends Comparable<?>>,具体取决于使用上下文,因为它们都是常见的泛型超类型)和运行时类型的原始ArrayList(因为它是常见的原始超类型)。

例如(test it yourself)

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

答案 3 :(得分:5)

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

尝试:

Write(two > six ? two.ToString() : "6");

答案 4 :(得分:5)

在Java和C#(以及大多数其他语言)中,表达式的结果都有一个类型。对于三元运算符,有两个可能的子表达式针对结果进行求值,并且两者必须具有相同的类型。对于Java,可以通过自动装箱将int变量转换为Integer。既然IntegerString都继承自Object,它们可以通过简单的缩小转换转换为相同的类型。

另一方面,在C#中,int是基元,并且没有隐式转换为string或任何其他object