Under the Hood - Ternary Operator [ ?: ]

Someone posted the following question on Stack Overflow today: (since deleted)

Is there a better way according to best practices to do this type of ternary operation without breaking it down into an if block?

visitorInfo = string.IsNullOrEmpty(visitorInfo.latitude) ?

 DetermineGeolocation(visitorInfo) : visitorInfo;

Meaning, without breaking it into a complete if block, is there a way to avoid saying visitorInfo = visitorInfo on a false condition? Maybe something like: DoNothing() or some similar coding construct?


I made the following comment, which may not have seemed entirely helpful to him, but it’s accurate.

A ternary operation is equivalent to an if/else statement. If you don’t want/need the else portion, then this construct is not appropriate for your situation, and you should just stick with an if.

This isn’t the first time I’ve seen a question similar to this one on SO. The issue is a misunderstanding of what code the ternary (or conditional) operator is meant to streamline. Referring to the msdn doc for the ?: operator:

The conditional operator (?:) returns one of two values depending on the value of a Boolean expression. Following is the syntax for the conditional operator.

condition ? first_expression : second_expression;

The condition must evaluate to true or false. If condition is true, first_expression is evaluated and becomes the result. If condition is false, second_expression is evaluated and becomes the result. Only one of the two expressions is evaluated.

The OP is essentially requesting a way use the ternary operator with a first_expression, while ignoring the second_expression.

When converting his ternary operation…

visitorInfo = string.IsNullOrEmpty(visitorInfo.latitude)  
                ? DetermineGeolocation(visitorInfo)
                : visitorInfo;

… to the equivalent if/else statement…

if (string.IsNullOrEmpty(visitorInfo.latitude))  
    visitorInfo = DetermineGeolocation(visitorInfo);
else  
    visitorInfo = visitorInfo;

… you can easily see what needs to happen.

He doesn’t need a ternary operator at all – just a single if statement:

if (string.IsNullOrEmpty(visitorInfo.latitude))  
    visitorInfo = DetermineGeolocation(visitorInfo);

Because I’m bored, I wanted to see what the generated IL looks like for a ternary operator vs an if/else construct. Are they really the same?

I created a Class Library project with the following two methods, which are functionally identical. They accept the same input, make the same comparison and assign the same results.

public void Method1(string name)  
{
    string gender = name == "Mary" ? "Woman" : "Man";
}

public void Method2(string name)  
{
    string gender;
    if (name == "Mary")
        gender = "Woman";
    else
        gender = "Man";
}

I then compiled and used ILDASM to inspect the resulting IL (Erik Dietrich wrote a good introduction to ILDASM as part of another post), and got the following instruction output.

First, the ternary operation:

.method public hidebysig instance void  Method1(string name) cil managed
{
  // Code size       29 (0x1d)
  .maxstack  2
  .locals init ([0] string gender)
  IL_0000:  nop
  IL_0001:  ldarg.1
  IL_0002:  ldstr      "Mary"
  IL_0007:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                 string)
  IL_000c:  brtrue.s   IL_0015
  IL_000e:  ldstr      "Man"
  IL_0013:  br.s       IL_001a
  IL_0015:  ldstr      "Woman"
  IL_001a:  nop
  IL_001b:  stloc.0
  IL_001c:  ret
} // end of method TernaryTester::Method1

What’s it doing?

  • Push the value of the name parameter and the string “Mary” onto the stack.Pops the last two strings from the stack, and compares them (op_Equality).
  • If the two strings (name and “Mary”) are equal, jump to IL0015, which pushes “Woman” onto the stack.Otherwise, push “Man” onto the stack and jump to IL001a.
  • Either way, pop the last string (“Woman” or “Man”) from the stack and return.

Secondly, the traditional if/else statement:

.method public hidebysig instance void  Method2(string name) cil managed
{
  // Code size       34 (0x22)
  .maxstack  2
  .locals init ([0] string gender,
           [1] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  ldarg.1
  IL_0002:  ldstr      "Mary"
  IL_0007:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                 string)
  IL_000c:  ldc.i4.0
  IL_000d:  ceq
  IL_000f:  stloc.1
  IL_0010:  ldloc.1
  IL_0011:  brtrue.s   IL_001b
  IL_0013:  ldstr      "Woman"
  IL_0018:  stloc.0
  IL_0019:  br.s       IL_0021
  IL_001b:  ldstr      "Man"
  IL_0020:  stloc.0
  IL_0021:  ret
} // end of method TernaryTester::Method2

What’s it doing?

  • Push the value of the name parameter and the string “Mary” onto the stack.Pops the last two strings from the stack, and compares them (op_Equality). (So far, the same.)
  • Push 0 onto the stack and compare 0 to the result of the previous operation. If the op_Equality returned false (name != “Mary”), then push 1 onto the stack; otherwise 0.(Basically just flipping the comparison result around from the way the ternary operation worked. The purpose of stloc.1 immediately followed by ldloc.1 escapes me. It seems to be popping the value from the stack just to push it back on.)
  • If the value of ceq was 1, and the two strings (name and “Mary”) are NOT equal, jump to IL001b, which pushes “Man” onto the stack. Otherwise, push “Woman” onto the stack and jump to IL0021.(Making the same comparisons as the ternary operation, but evaluating inequality between name and “Mary” first, instead of equality)
  • Either way, pop the last string (“Woman” or “Man”) from the stack and return. (Same as before.)

The IL code is a bit different, and actually longer with more steps in the case of the if/else statement. One compares equality first, while the other compares inequality; but functionally, the ternary operator is doing the same thing as the if/else statement.

And to answer the original question (again), no, there is no way to use the ternary operator while omitting the second_expression. What you end up with is the if portion of an if/else statement.


If you’re interested, you can see a full list of CIL instructions.

Subscribe to Weekly Updates!

Get an email with the latest posts, once per week...
* indicates required