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

我是一个C#新手,我只是遇到一个问题。 处理三元运算符( ? : :)时,C#和Java是有区别的。

在下面的代码段中,为什么第四行不起作用? 编译器显示there is no implicit conversion between 'int' and 'string'的错误消息。 第五行不起作用。 两个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#中缺less什么语言function? 如果有的话,为什么不添加?

查看C#5语言规范第7.14节:条件运算符,我们可以看到以下内容:

  • 如果x有X型,y有Y型

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

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

    • 否则,不能确定expression式types,并发生编译时错误

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

Java 7语言规范第15.25节:有条件的运算符

  • 如果第二个和第三个操作数具有相同的types(可能是空types),那么这就是条件expression式的types。 ( NO
  • 如果第二个和第三个操作数中的一个是原始typesT,而另一个是对T进行装箱转换(第5.1.7节)的结果,则条件expression式的types是T.(
  • 如果第二个和第三个操作数中的一个是空types,而另一个的types是引用types,那么条件expression式的types就是该引用types。 ( NO
  • 否则,如果第二个和第三个操作数具有可转换types(§5.1.8)为数字types,则有以下几种情况:( NO
  • 否则,第二个和第三个操作数分别是S1和S2的types。 假设T1是将装箱转换为S1所得到的types,并且让T2是将装箱转换应用于S2所得到的types。
    条件expression式的types是将采集转换(§5.1.10)应用于lub(T1,T2)(§15.12.2.7)的结果。 ( YES

而且,看第15.12.2.7节。 根据实际的参数推断types参数,我们可以看到它试图find一个共同的祖先,将作为用于使用Object的调用的types。 Object 一个可以接受的参数,所以调用将起作用。

给出的答案是好的; 我想补充一点,C#的这个规则是更为一般的devise准则的结果。 当被要求从几个选项中的一个推断出expression式的types时, C#select它们中唯一最好的一个 。 也就是说,如果你给C#一些select,如“长颈鹿,哺乳动物,动物”,那么它可能会select最一般的动物 – 或者可能select最具体的 – 长颈鹿 – 取决于具体情况。 但它必须select其中一个实际的select 。 C#从来不说“我的select是在猫和狗之间,因此我会推断动物是最好的select”。 这不是一个select,所以C#不能select它。

在三元运算符的情况下,C#尝试select更一般的int和stringtypes,但都不是更一般的types。 C#不是select一个不是首选的types,而是像对象一样,C#决定不能推断出任何types。

我还注意到,这符合C#的另一个devise原则:如果出现错误,请告诉开发人员。 这个语言不会说“如果可以的话,我会猜测你的意思和混乱。 该语言说:“我认为你写了一些令人困惑的东西,我会告诉你的。”

另外,我注意到C#不会将variables推理到赋值 ,而是另一个方向。 C#没有说“你正在分配给一个对象variables,因此expression式必须可以转换为对象,因此我将确保它是”。 相反,C#说“这个expression式必须有一个types,我必须能够推断出这个types与对象兼容”。 由于expression式不具有types,因此会产生错误。

关于generics部分:

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

在C#中,编译器试图右边的expression式部分转换成一些常见的types; 由于List<int>List<string>是两个不同的构造types,因此不能将其转换为另一个。

在Java中,编译器试图find一个通用的超types而不是转换,所以编译的代码涉及通配符和types擦除的隐式使用;

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

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

例如(自己testing) ,

 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 

在Java和C#(以及大多数其他语言)中,expression式的结果都是一个types。 在三元运算符的情况下,有两个可能的子expression式对结果进行评估,并且两者必须具有相同的types。 在Java的情况下, intvariables可以通过自动装箱转换为Integer 。 既然IntegerString都是从Objectinheritance的,那么可以通过简单的缩小转换将它们转换成相同的types。

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

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

尝试:

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