Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between C# and Java's ternary operator (? :)

I am a C# newbie and I just encounter a problem. There is a difference between C# and Java when dealing with the ternary operator (? :).

In the following code segment, why does the 4th line not work? The compiler shows an error message of there is no implicit conversion between 'int' and 'string'. The 5th line does not work as well. Both Lists are objects, aren't they?

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 

However, the same code works in 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 

What language feature in C# is missing? If any, why is it not added?

like image 392
blackr1234 Avatar asked Feb 05 '16 18:02

blackr1234


2 Answers

Looking through the C# 5 Language Specification section 7.14: Conditional Operator we can see the following:

  • If x has type X and y has type Y then

    • If an implicit conversion (§6.1) exists from X to Y, but not from Y to X, then Y is the type of the conditional expression.

    • If an implicit conversion (§6.1) exists from Y to X, but not from X to Y, then X is the type of the conditional expression.

    • Otherwise, no expression type can be determined, and a compile-time error occurs

In other words: it tries to find whether or not x and y can be converted to eachother and if not, a compilation error occurs. In our case int and string have no explicit or implicit conversion so it won't compile.

Contrast this with the Java 7 Language Specification section 15.25: Conditional Operator:

  • If the second and third operands have the same type (which may be the null type), then that is the type of the conditional expression. (NO)
  • If one of the second and third operands is of primitive type T, and the type of the other is the result of applying boxing conversion (§5.1.7) to T, then the type of the conditional expression is T. (NO)
  • If one of the second and third operands is of the null type and the type of the other is a reference type, then the type of the conditional expression is that reference type. (NO)
  • Otherwise, if the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases: (NO)
  • Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing conversion to S2.
    The type of the conditional expression is the result of applying capture conversion (§5.1.10) to lub(T1, T2) (§15.12.2.7). (YES)

And, looking at section 15.12.2.7. Inferring Type Arguments Based on Actual Arguments we can see it tries to find a common ancestor that will serve as the type used for the call which lands it with Object. Object is an acceptable argument so the call will work.

like image 90
Jeroen Vannevel Avatar answered Oct 13 '22 11:10

Jeroen Vannevel


The given answers are good; I would add to them that this rule of C# is a consequence of a more general design guideline. When asked to infer the type of an expression from one of several choices, C# chooses the unique best of them. That is, if you give C# some choices like "Giraffe, Mammal, Animal" then it might choose the most general -- Animal -- or it might choose the most specific -- Giraffe -- depending on the circumstances. But it must choose one of the choices it was actually given. C# never says "my choices are between Cat and Dog, therefore I will deduce that Animal is the best choice". That wasn't a choice given, so C# cannot choose it.

In the case of the ternary operator C# tries to choose the more general type of int and string, but neither is the more general type. Rather than picking a type that was not a choice in the first place, like object, C# decides that no type can be inferred.

I note also that this is in keeping with another design principle of C#: if something looks wrong, tell the developer. The language does not say "I'm going to guess what you meant and muddle on through if I can". The language says "I think you've written something confusing here, and I'm going to tell you about that."

Also, I note that C# does not reason from the variable to the assigned value, but rather the other direction. C# does not say "you're assigning to an object variable therefore the expression must be convertible to object, therefore I will make sure that it is". Rather, C# says "this expression must have a type, and I must be able to deduce that the type is compatible with object". Since the expression does not have a type, an error is produced.

like image 29
Eric Lippert Avatar answered Oct 13 '22 09:10

Eric Lippert