The compiler does not infer the type of the result of the conditional operator from the usage of the result, but from the types of its arguments. The compiler fails when it sees this expression because it cannot deduce the type of the result:
IsDateTimeHappy(myDateTime) ? null : myDateTime;
Since null
and DateTime
are not compatible, you need to tell the compiler what the type should be. A cast should do the trick:
DateTime? x = IsDateTimeHappy(myDateTime) ? (DateTime?)null : myDateTime;
OutputDateTime(x);
Now the compiler will have no problems. You can also write the above on one line if you prefer (but I would probably not do this):
OutputDateTime(IsDateTimeHappy(myDateTime) ? (DateTime?)null : myDateTime);
Eric Lippert has a good answer that is also relevant here and goes into more details about how the compiler determines types.
null
can represent any object-based datatype. You need to cast null
as a datatype so it know what you are talking about.
int? x = (value.HasValue) ? value.Value : (int?)null;
I know, it sounds a bit strange.
To answer the questions in the comments:
Why is it not implicit though?
Yeah, I get that. But why do I not have to cast it in a If Else block?
Let's walk through the code.
Your else
statement looks like this:
else x = null;
This means you are assigning the value of null
to x
. This is valid, because x
is a int?
, which takes nulls
.
The difference comes when you have the ternary operator. It says: "assign the value of the operator into x
". The question (and the reason for your error) is, what datatype is the result of the ternary operator?
From your code, you can't be sure, and the compiler throws its hands up.
int? x = (value.HasValue) ? value.Value : null;
// int? bool int ??
What datatype is null
? You are quick to say "well it's a int?
, because the other side is a int
and the result is a int?
". The problem is, what about the following:
string a = null;
bool? b = null;
SqlConnectionStringBuilder s = null;
This is also valid, which means null
can be used for any object-based datatype
. This is why you have to explicitly cast null
as the type you want to use, because it can be used for anything!
Another explanation (and possible more accurate):
You can't have an implicit cast between a nullable and a non-nullable value.
int
is not-nullable (it's a structure), where null
is. This is why in Habib's answer you can put the cast on either the left or right side.
Best Answer
The spec (§7.14) says that for conditional expression
b ? x : y
, there are three possibilities, eitherx
andy
both have a type and certain good conditions are met, only one ofx
andy
has a type and certain good conditions are met, or a compile-time error occurs. Here, "certain good conditions" means certain conversions are possible, which we will get into the details of below.Now, let's turn to the germane part of the spec:
The issue here is that in
only one of the conditional results has a type. Here
x
is anint
literal, andy
isnull
which does not have a type andnull
is not implicitly convertible to anint
1. Therefore, "certain good conditions" aren't met, and a compile-time error occurs.There are two ways around this:
Here we are still in the case where only one of
x
andy
has a type. Note thatnull
still does not have a type yet the compiler won't have any problem with this because(int?)5
andnull
are both implicitly convertible toint?
(§6.1.4 and §6.1.5).The other way is obviously:
but now we have to read a different clause in the spec to understand why this is okay:
Here
x
is of typeint
andy
is of typeint?
. There is no implicit conversion fromint?
toint
, but there is an implicit conversion fromint
toint?
so the type of the expression isint?
.1: Note further that the type of the left-hand side is ignored in determining the type of the conditional expression, a common source of confusion here.