Search code examples
c#cildisassembly

CIL disassembly of a function parameter involving a ternary expression


I struggle with the CIL disassembly of the parameter to the WriteLine() call in:

FileInfo path = new FileInfo(@"c:\a.txt");
Console.WriteLine("prefix: " + path != null ? path.FullName : "null");

CIL dissasembly

.locals init (
    [0] class [mscorlib]System.IO.FileInfo path
)

// ...

IL_000c: ldstr "prefix: "
IL_0011: ldloc.0
IL_0012: call string [mscorlib]System.String::Concat(object, object)
IL_0017: brtrue.s IL_0020

IL_0019: ldstr "null"
IL_001e: br.s IL_0026

IL_0020: ldloc.0
IL_0021: callvirt instance string [mscorlib]System.IO.FileSystemInfo::get_FullName()

IL_0026: call void [mscorlib]System.Console::WriteLine(string)

It seems to me that Concat is called first, and only then is the ternary operator evaluated. In particular:

IL_0012 Concat("prefix", path) seems to be called,
IL_0012 brtrue.s IL_0020 // branch based on the previous return value

  • Why is Concat called with path as a parameter instead of path.FullName?
  • Does Concat return null, if its first parameter is null? How does the compiler know that? (If I replace + with my own plus(string, string), the disassembly to something I would rather expect.)

Could you please explain, how the disassembly handles the ternary parameter and the call to WriteLine?


Solution

  • It's not a problem of the disassembly, it's a problem of operator precedence.

    Your expression is evaluated as

    ("prefix: " + path != null) ? path : "null";
    

    Not as

    "prefix: " + (path != null ? path : "null");
    

    as you seem to expect. Just use parentheses correctly and you'll be fine :)

    In fact, it's a missed "optimization" on part of the compiler - since string.Concat("prefix: ", whatever) can never be null, you'll always get path.FullName. Of course, a ternary that only ever evaluates to one of its options is almost certainly a bug, so...