I am trying to use the conditional operator, but I am getting hung up on the type it thinks the result should be.
Below is an example that I have contrived to show the issue I am having:
class Program
{
public static void OutputDateTime(DateTime? datetime)
{
Console.WriteLine(datetime);
}
public static bool IsDateTimeHappy(DateTime datetime)
{
if (DateTime.Compare(datetime, DateTime.Parse("1/1")) == 0)
return true;
return false;
}
static void Main(string[] args)
{
Date开发者_Python百科Time myDateTime = DateTime.Now;
OutputDateTime(IsDateTimeHappy(myDateTime) ? null : myDateTime);
Console.ReadLine(); ^
} |
} |
// This line has the compile issue ---------------+
On the line indicated above, I get the following compile error:
Type of conditional expression cannot be determined because there is no implicit conversion between '< null >' and 'System.DateTime'
I am confused because the parameter is a nullable type (DateTime?). Why does it need to convert at all? If it is null then use that, if it is a date time then use that.
I was under the impression that:
condition ? first_expression : second_expression;
was the same as:
if (condition)
first_expression;
else
second_expression;
Clearly this is not the case. What is the reasoning behind this?
(NOTE: I know that if I make "myDateTime" a nullable DateTime then it will work. But why does it need it?
As I stated earlier this is a contrived example. In my real example "myDateTime" is a data mapped value that cannot be made nullable.)
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.
The reason is the ternary operator expects both the operands to be of the same type. The whole operator get worked out BEFORE it is assigned to a result (in this case passed into a function), so the compiler can't know what the result type is.
IsDateTimeHappy(myDateTime) ? null : myDateTime
In the above case there is no conversion path between null
and DateTime
. As soon as you cast one of them to DateTime?
, the compiler can convert the other one:
IsDateTimeHappy(myDateTime) ? (DateTime?)null : myDateTime
//OR
IsDateTimeHappy(myDateTime) ? null : (DateTime?)myDateTime
The fist line of code above works because the compiler can convert DateTime
to DateTime?
via an implicit conversion operator:
//In Nullable<T>
public static implicit operator T?(T value);
The second line works because null
can be assigned to DateTime?
since the latter is a reference type.
The implicit conversion is not allowed by the return statement. If you had
if (condition)
return first_expression;
else
return second_expression;
Then you'd be comparing apples to apples. And you'd have no problems - as you stated.
In your case, you're allocated so much space on the stack for a DateTime - which is a non-nullable value type. So you're making a statement that doesn't make any sense to the compiler. If you say, I'm going to pass you an A
or a B
, then the A
and the B
need to be the same thing. In your case, the B
can never be an A
.
What the compiler is saying is:
If
IsDateTimeHappy(myDateTime)
isfalse
, then I need to return a value of typeDateTime
equal tomyDateTime
. If it istrue
, then I need to return a value equal tonull
, but you haven't told me what type it should be!
That's why Mark's answer is the solution. After you provide a cast telling the compiler what type of value will be returned if the condition is true
, it can check whether the true
and false
return values can be converted to (or are of) the same type.
Cheers Mark! ;-)
Instead of null
use default(DateTime?)
and then both sides of the ternary will have compatible types.
精彩评论