Required Casting with C# Ternary Conditional Operator

c++castingternary-operator

I am trying to figure out why casts are required in the following examples:

bool test = new Random().NextDouble() >= 0.5;
short val = 5;

// Ex 1 - must cast 0 to short
short res = test ? 5 : 0;  // fine
short res = test ? val : 0;  // error
short res = test ? val : (short)0;  // ugly

// Ex 2 - must cast either short or null to short?
short? nres = test ? val : null;  // error
short? nres = test ? (short?)val : null;  // ugly
short? nres = test ? val : (short?)null;  // ugly
short? nres = test ? val : default(short?);  // ugly

The first example just seems crazy to me. If short i = 0; compiles, why can't the compiler implicitly treat the 0 (or any other valid short value) as a short in the above code?

The second example makes more sense to me. I understand that the compiler is unable to determine the type of the expression on the right side of the =, but IMO it should take nullable types into account when doing so.

I'd like to understand if there is actual reasoning behind these compiler errors.

Best Answer

The expression test ? val : 0 compiles just fine. You get an error in this line, because the type of this expression is int and you're trying to assign it to a short variable. That requires an explicit cast. From C# language spec:

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.

Another question is why, for example, a literal 0 can be assigned to a short variable without a cast:

short i = 0;

And a result of ternary operator has to be cast:

bool test = new Random().NextDouble() >= 0.5;
short val = 5;
short i = (short)(test ? val : 0);

The reason is that first assignment is evaluated at compile-time, because it consists only of constants. In such case, the implicit constant expression conversion rules apply:

• A constant-expression (§7.19) of type int can be converted to type sbyte, byte, short, ushort, uint, or ulong, provided the value of the constant-expression is within the range of the destination type.

The ternary operator can also be evaluated at compile time if all operands are constants:

short i = true ? 0 : int.MaxValue;

In any other case, the stricter runtime conversion rules apply. All 3 statements below give compile errors:

int intVal = 0;
short shortVal = 0;
bool boolVal = true;

short i = true ? 0 : intVal;
short j = true ? shortVal : 0;
short k = boolVal ? 0 : 0;

For reference you can see Eric Lippert's comments.

The second example would require treating Nullable<> as a special case, like you already noticed.