Question

Ahh, don't you just love a good ternary abuse? :) Consider the following expression:

true ? true : true ? false : false

For those of you who are now utterly perplexed, I can tell you that this evaluates to true. In other words, it's equivalent to this:

true ? true : (true ? false : false)

But is this reliable? Can I be certain that under some circumstances it won't come to this:

(true ? true : true) ? false : false

Some might say - well, just add parenthesis then or don't use it altogether - after all, it's a well known fact that ternary operators are evil!

Sure they are, but there are some circumstances when they actually make sense. For the curious ones - I'm wring code that compares two objects by a series of properties. It would be pretty nice if I cold write it like this:

obj1.Prop1 != obj2.Prop1 ? obj1.Prop1.CompareTo(obj2.Prop1) :
obj1.Prop2 != obj2.Prop2 ? obj1.Prop2.CompareTo(obj2.Prop2) :
obj1.Prop3 != obj2.Prop3 ? obj1.Prop3.CompareTo(obj2.Prop3) :
obj1.Prop4.CompareTo(obj2.Prop4)

Clear and concise. But it does depend on the ternary operator associativity working like in the first case. Parenthesis would just make spaghetti out of it.

So - is this specified anywhere? I couldn't find it.

Was it helpful?

Solution

Yes, you can rely on this (not only in C# but in all (that I know) other languages (except PHP … go figure) with a conditional operator) and your use-case is actually a pretty common practice although some people abhor it.

The relevant section in ECMA-334 (the C# standard) is 14.13 §3:

The conditional operator is right-associative, meaning that operations are grouped from right to left. [Example: An expression of the form a ? b : c ? d : e is evaluated as a ? b : (c ? d : e). end example]

OTHER TIPS

If you have to ask, don't. Anyone reading your code will just have to go through the same process you did, over and over again, any time that code needs to be looked at. Debugging such code is not fun. Eventually it'll just be changed to use parentheses anyway.

Re: "Try to write the whole thing WITH parentheses."

result = (obj1.Prop1 != obj2.Prop1 ? obj1.Prop1.CompareTo(obj2.Prop1) :
         (obj1.Prop2 != obj2.Prop2 ? obj1.Prop2.CompareTo(obj2.Prop2) :
         (obj1.Prop3 != obj2.Prop3 ? obj1.Prop3.CompareTo(obj2.Prop3) :
                                     obj1.Prop4.CompareTo(obj2.Prop4))))

Clarification:

  • "If you have to ask, don't."
  • "Anyone reading your code..."

Following the conventions common in a project is how you maintain consistency, which improves readability. It would be a fool's errand to think you can write code readable to everyone—including those who don't even know the language!

Maintaining consistency within a project, however, is a useful goal, and not following a project's accepted conventions leads to debate that detracts from solving the real problem. Those reading your code are expected to be aware of the common and accepted conventions used in the project, and are even likely to be someone else working directly on it. If they don't know them, then they are expected to be learning them and should know where to turn for help.

That said—if using ternary expressions without parentheses is a common and accepted convention in your project, then use it, by all means! That you had to ask indicates that it isn't common or accepted in your project. If you want to change the conventions in your project, then do the obviously unambiguous, mark it down as something to discuss with other project members, and move on. Here that means using parentheses or using if-else.

A final point to ponder, if some of your code seems clever to you:

Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it. — Brian W. Kernighan

The assertion that parentheses detract from the readability of the code is a false assumption. I find the parenthetical expression much more clear. Personally, I would use the parentheses and/or reformat over several lines to improve readability. Reformatting over several lines and using indenting can even obviate the need for parentheses. And, yes, you can rely on the fact that the order of association is deterministic, right to left. This allows the expression to evaluate left to right in the expected fashion.

obj1.Prop1 != obj2.Prop1
     ? obj1.Prop1.CompareTo(obj2.Prop1)
     : obj1.Prop2 != obj2.Prop2
           ? obj1.Prop2.CompareTo(obj2.Prop2)
           : obj1.Prop3 != obj2.Prop3
                  ? obj1.Prop3.CompareTo(obj2.Prop3)
                  : obj1.Prop4.CompareTo(obj2.Prop4);

Refer to msdn: http://msdn.microsoft.com/en-us/library/ty67wk28%28VS.80%29.aspx

"If condition is true, first expression is evaluated and becomes the result; if false, the second expression is evaluated and becomes the result. Only one of two expressions is ever evaluated."

x = cond1 ? result1
  : cond2 ? result2
  : cond3 ? result3
  : defaultResult;

vs

if (cond1) x = result1;
else if (cond2) x = result2;
else if (cond3) x = result3;
else x = defaultResult;

I like the first one.

Yes, you can rely on conditional operator associativity. Its in the manual, at the link kindly provided by dcp, stated as "The conditional operator is right-associative", with an example. And, as you suggested and I and others agreed, the fact that you can rely on it allows clearer code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top