views:

248

answers:

6

The following code does not compile:

//int a = ...
int? b = (int?) (a != 0 ? a : null);

In order to compile, it needs to be changed to

int? b = (a != 0 ? a : (int?) null);

Since both b = null and b = a are legal, this doesn't make sense to me.

Why do we have to cast the null into an int? and why can't we simply provide an explicit type cast for the whole expression (which I know is possible in other cases)?

+1  A: 

it's because when you use that kind of notation, both member must be of the same type, so you have to explicitly say "this member is a int?".

is it clear ?

remi bourgarel
-1: they don't need to be of the same type, one just needs to be castable to the other's type: see above, @nobugz and @Vlad.
ANeves
@srpt, i wanted to vote down too, but then i saw you didn't really downvote and undid my downvote.
Johannes Schaub - litb
=/ I what? *[monocle pops]* Curses! I undid the downvote because I changed my mind, but forgot to edit the `-1:` at the start of the comment - pardon! (I changed my mind because it's not a wrong answer, just not very accurate or clear.)
ANeves
+5  A: 

The both alternatives of ?: operator must be of the same type. Otherwise, the compiler cannot deduce the type of the whole conditional expression.

null is not an int, so you need to give a hint to the compiler that the resulting type is int?.


Edit: as the others pointed out, the two types don't need to be the same, but one of them should be castable to another (that another one will be the result type). See specs for more details.

Vlad
+1  A: 

When you use the conditional operator with operands of different types, the compiler will check whether one of the types can be implicitly converted to the other type.

If neither type can be implicitly converted to the other one, it will give an error, even if there is a third type that they can both implicitly convert to. The compiler will not insert an implicit conversion on both sides at once.

In your case, both int and null requires implicit conversions to become int?, so it doesn't work.
You need to change your code so that one side is an int? and the other side can be implicitly converted to int?. The simplested way to do this is to replace null with new int?(), like this:

int? b = (a != 0 ? a : new int?());

This only requires one implicit conversion (int to int?).

SLaks
+11  A: 

From chapter 7.13 of the C# Language Specification:

The second and third operands of the ?: operator control the type of the conditional expression. Let X and Y be the types of the second and third operands. Then,

  • If X and Y are the same type, then this is the type of the conditional expression.
  • Otherwise, 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.
  • Otherwise, 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 your case, there is no implicit conversion from int to null nor the other way around. Your cast solves the problem, int is convertible to int?

Hans Passant
Ah, I see. I wouldn't think both expressions should need to have the same type, though, as long as they both both implicitly cast to the assigned type - what happens if I say `Animal animal = (condition ? new Cat() : new Dog())` ?
BlueRaja - Danny Pflughoeft
Then you'll also get a compiler error. (Unless you cast either side to `Animal`)
SLaks
The left-hand side of the assignment statement does not affect the type of the expression. Cats are not implicitly convertible to dogs, my wife insists.
Hans Passant
I hope some future C# version will use more intelligent type inference. C# can already do that when calling a generic method "T M<T>(T a, T b)", so why not when using "c?a:b" ?
Daniel
What type would you infer when you can choose between int and null? If you say "int? of course!" then what do you do when the expression is assigned to an int. You'll let it bomb at runtime? Using a hidden type that isn't visible anywhere is not the C# way.
Hans Passant
@Daniel: Actually, type-inference doesn't do this either. Try `void SomeMethod<T>(T a, T b) {} ... SomeMethod(2, null)`. It will not work for any pair of classes which have a common parent but are not directly castable to one-another (presumably because otherwise a mistake would cause a `What<object>` rather than a compilation error)
BlueRaja - Danny Pflughoeft
+3  A: 

You can save yourself from casting if you use default(int?) instead of null.

int a = 42;
int? b = (a != 0 ? a : default(int?));
statenjason
A: 

This is pretty much a duplicate of one of my questions, regarding the conditional operator. It's not the null, it really is the :.

The answer accepted there is pretty good, from none other than Eric Lippert, who is on the C# compiler team.

MPelletier