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

本文关键字:之间 区别 运算符 Java 三元 | 更新日期: 2023-09-27 18:33:28

我是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# 中缺少哪些语言功能?如果有,为什么不添加?

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

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

  • 如果 x 具有类型 X,y 具有 Y 类型,则

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

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

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

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

将其与 Java 7 语言规范第 15.25 节:条件运算符进行对比:

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

并且,查看第 15.12.2.7 节。根据实际参数推断类型参数,我们可以看到它试图找到一个共同的祖先,该祖先将用作用于调用的类型,该调用将其与 Object 一起着陆。 Object是一个可接受的参数,因此调用将起作用。

给出的答案很好;我想补充一点,C# 的这个规则是更通用的设计指南的结果。当被要求从多个选项之一推断表达式的类型时,C# 会选择其中唯一的最佳表达式。 也就是说,如果为 C# 提供一些选择,例如"长颈鹿、哺乳动物、动物",那么它可能会选择最一般的 - 动物 - 或者可能会选择最具体的 - 长颈鹿 - 具体取决于情况。但它必须选择实际给予的选择之一。 C#从不说"我的选择是在猫和狗之间,因此我会推断动物是最好的选择"。 这不是一个给定的选择,所以 C# 不能选择它。

对于三元运算符,C# 尝试选择更通用的 int 和字符串类型,但也不是更通用的类型。与其选择一开始就不是选择的类型(如对象(,C# 决定无法推断任何类型。

我还注意到,这符合 C# 的另一个设计原则:如果有什么看起来不对劲,请告诉开发人员。语言没有说"我会猜到你的意思,如果可以的话,我会糊里糊涂"。语言说:"我想你在这里写了一些令人困惑的东西,我要告诉你。

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

关于泛型部分:

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

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

在 Java 中,编译器试图找到一个通用的超类型而不是转换,因此代码的编译涉及通配符的隐式使用和类型擦除;

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

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

例如(自己测试(,

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#(以及大多数其他语言(中,表达式的结果都有一个类型。对于三元运算符,有两个可能的子表达式为结果求值,并且它们必须具有相同的类型。在 Java 的情况下,int变量可以通过自动装箱转换为Integer。现在由于IntegerString都继承自Object,它们可以通过简单的缩小转换转换为相同的类型。

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

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

尝试:

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