Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type result with conditional operator in C#

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)
    {
        DateTime 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.)

like image 789
Vaccano Avatar asked May 11 '10 23:05

Vaccano


3 Answers

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.

like image 57
Mark Byers Avatar answered Nov 16 '22 07:11

Mark Byers


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.

like image 2
Igor Zevaka Avatar answered Nov 16 '22 09:11

Igor Zevaka


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.

like image 1
Jarrett Meyer Avatar answered Nov 16 '22 08:11

Jarrett Meyer