Type inference and the conditional operator

The C# conditional operator is a kind of shorthand for an if...else statement. For example, this code:

int result;
if (y == 0)
    result = 0;
else
    result = x/y;

Can be rewritten using the conditional operator:

int result = (y == 0) ? 0 : x/y;

Terseness isn’t a goal in and of itself, but very often the conditional operator really does make code more readable.

This morning I was working on code that will produce output in XML or JSON, depending on a command line parameter. I created a delegate, two methods (one for each output type), and code to assign it, like this:

delegate void OutputRecordDelegate(MediaRecord rec);
OutputProcDelegate OutputProc = null;

void OutputRecordXml(MediaRecord rec)
{
}

void OutputRecordJson(MediaRecord rec)
{
}

// code to initialize
if (OutputFormat == "xml")
    OutputProc = OutputRecordXml;
else
    OutputProc = OutputRecordJson;

That all works as expected. It compiles and runs just fine. But this is exactly the kind of code that I think is more readable when written with the conditional operator, like this:

OutputProc = (OutputFormat == "xml") ? OutputRecordXml : OutputRecordJson;

Imagine my surprise when the compiler issued an error message:

Type of conditional expression cannot be determined because
there is no implicit conversion between 'method group' and 'method group'

Now why is that? It looks right!

The problem has to do with type inference. In the statement:

OutputProc = OutputRecordXml;

The compiler uses type inference to convert OutputRecordXml to the proper type: OutputRecordDelegate. It’s as if I had written:

OutputProc = new OutputRecordDelegate(OutputXml);

The compiler has enough information and the smarts to infer the type and do the proper conversion.

But the specification for the conditional operator doesn’t allow that. The rules for type conversion in the conditional operator are a bit different:

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.

Since there is no implicit conversion that will convert between the two methods, the compiler issues an error message.

I can rewrite the code to do the explicit conversions:

OutputProc = (OutputFormat == "xml") ?
    (OutputRecordDelegate)OutputXml :
    (OutputRecordDelegate)OutputJson;

Whereas that works, having to do the explicit casts eliminates most of the benefit of using the conditional operator. The simple version (the one that doesn’t work) is, to me, unquestionably more readable than the standard if...else construct. Having to add the casts makes the code a bit awkward, and I see little or no benefit as a result.