Is there a difference between a ternary operator and an if statement in C#? [duplicate]

StackOverflow https://stackoverflow.com/questions/3877773

  •  28-09-2019
  •  | 
  •  

Question

I'm working with a nullable DateTime object and ran into some strange behavior. Here's a sample function:

    public DateTime? Weird()
    {
        DateTime check = DateTime.Now;
        DateTime? dt;
        if (check == DateTime.MinValue)
            dt = null;
        else
            dt = Viewer.ActiveThroughUTC.ToLocalTime();

        //this line give a compile error
        dt = (check == DateTime.MinValue) ? (null) : (Viewer.ActiveThroughUTC.ToLocalTime());
        return dt;
    }

As far as I know, the line with the ternary operator should be the same as the preceding four lines, but VS2010 is giving me a compile error, saying that no conversion exists between <null> and DateTime (even though the object in question is a 'DateTime?'). Is there something I should know about the ternary operator or is this (gasp?) a bug?

Was it helpful?

Solution

Both elements in the ?: operator should be of the same type (but don't have to be - see the details below). Cast null to DateTime?:

dt = (check == DateTime.MinValue) ? (DateTime?)null : ...

From the spec:

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 (Section 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 (Section 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.

(Interestingly, it's not actually called the "ternary" operator. It's one possible ternary (three-valued) operator, and I'm not aware of any others in C#. It's called the "?:" operator, which is a little hard to pronounce. Also called the "conditional" operator.)

OTHER TIPS

Several answers have incorrectly stated that both values of the conditional operator must be the same type. This is decidedly untrue and is covered in detail in section 7.13 of the language spec

From the spec (X and Y are the types of the two values)

  • 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.

The second and third cases allow for the types to be different so long as there is an implicit conversion from one to the other (but not back).

The easiest way to fix this scenario is to explicitly cast one of the operands to DateTime?

DateTime? dt = (check == DateTime.MinValue) 
  ? (DateTime?)null
  : Viewer.ActiveThroughUTC.ToLocalTime();

Using the ternery operator the arguments have to be the same type. Change it to:

dt = (check == DateTime.MinValue) ? (DateTime?)null : 
        (DateTime?)Viewer.ActiveThroughUTC.ToLocalTime();   

the cast on the second argument (the real dateime) may not be necessary as there is an implicit cast fropm DateTime to DateTime?, and the first cast (DateTime?)null tells the compiler what to cast it to...

Use new DateTime?() instread of null. That way it knows what type the expression is supposed to be.

dt = check == DateTime.MinValue ? new DateTime?() : DateTime.Now;

A conditional statement's return options must be of the same type (or only one most be implicitly convertable), the implicit conversion to the nullable that the compiler infers with:

dt = null;

Doesn't happen here (if they differ should the first be converted to the second? or vice-versa?), so your return types from each option need to match or be convertable. For example this would work:

dt = check == DateTime.MinValue ? (DateTime?)null : Viewer.ActiveThroughUTC.ToLocalTime();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top