Ternary operator associativity in C# - can I rely on it?
Asked Answered
G

5

19

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.

Greengrocery answered 19/11, 2009 at 14:11 Comment(6)
Are you sure your code does what you mean? E.g. if obj1.Prop1 != obj2.Prop1 but obj1.Prop1.CompareTo(obj2.Prop1) == 0, your code will yield 0 instead of proceeding to check Prop2. Is it intended?Imprison
Just because you like the way it looks doesn't make it right, and certainly doesn't make it readable.. Anybody else looking at that code will have to at least mentally add parenthesis anyway, so why not do everybody a favor and refactor those lines of code.Popedom
atzz - The values are primitive types - this shouldn't happen unless there is a warp in spacetime. Miky D - suggestions? Keep in mind that this is for a lambda expression, so I'd like to keep it short. Also I'll add comments, which should clarify things for anyone looking at the code.Greengrocery
Well, every situation is different and without looking a the rest of the code I can't make any intelligent suggestions but maybe you could create a function that performs the comparisons and call that in the lambda..Popedom
I need to use it in one place only. I could, of course, extract it into a function, but there are many such instances in my code (it synchronizes two DB's) and I'd end up with a dozen such functions.Greengrocery
"Also I'll add comments, which should clarify things..." don't comment unreadable code. Make the code readable (where possible of course). It is certainly possible in this instance.Willowwillowy
S
25

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]

Sappington answered 19/11, 2009 at 14:14 Comment(6)
A note on readability. Anyone with a functional programming background will immediately recognize this (it looks almost precisely like Haskell’s guard clauses with appropriate formatting). So while it might not be universally acceptable and usable, it certainly is idiomatic in certain circles and may be very appropriate in some projects. On a more personal note, I use it in personal projects, I find it vastly more readable than chained ifs, and if you can’t read it then get out of my code.Sappington
"in certain circles [or projects]" is key!Gobo
You can't rely on it in PHP: #1921922Hothouse
Chased a bug for 4 hours that was caused by PHP's unique associativity rules for ternary (left to right). @Konrad: You should update the answer so that it's accurate in regards to PHPUpside
@Juan What, I should write that PHP sucks? That seems a bit off-topic. And it’s not very interesting … everybody knows that.Sappington
@Konrad: PHP does suck, but you're still giving misleading information in your answer, since you do know that PHP does not behave that way.Upside
G
16

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

Gobo answered 19/11, 2009 at 14:15 Comment(20)
It's for a lambda expression. Other ways would be only marginally better IMHO.Greengrocery
Absolutely agreed. If you have to ask, then it's not clear. If it's not clear, then put f-ing parentheses around it and stop being a clever programmer. That's where the worst bugs come from.Footstalk
I disagree that it is a good approach. It's not idiomatic and someone who has not encountered it needs to examine it closely to see what's going on. A series of if/then clauses is clearer.Species
yes, Anything I'M not familiar with, (or are too stuck in my ways to learn), must be 'clever' programming... –Schwa
Well... it's a fair point, of course. Would comments help? :) As I said, the reason for this is because I'm writing this in a lambda expression, so I'd like to keep it short, resembling a formula more than a piece of code.Greengrocery
Vilx: Why comments instead of parentheses? Which way is more obvious and least likely to result in docs/comments differing from code?Gobo
Try to write the whole thing WITH parentheses. Personally, I'd prefer the row of IF statements then. It's more verbose, but at least cleaner.Greengrocery
Charles: What do you have to judge by, except yourself? It's a fair assumption that if he had a hard time deciphering the code (and I use 'decipher' intentionally), then anyone else working on his project will too.Gobo
@Roger, no it's not a fair assumption. When I look at Lisp code I have no clue what it means. That doesn't mean it's bad code. Just cause someone doesn't like ternary operators, doesn't use them, and is therefore unfamiliar with reading them, doesn't mean they are unreadable... The same thing applies to lambdas the first few times I looked at them ... It's ok if someone doesn't want to use them, I can't (and wouldn't) try to force anyone to use anything they're uncomfortable with. Just don't disparage a tool because you don't like it and/or are not fluent or competent in it's use.Schwa
Do you often work on Lisp projects when you have no knowledge of Lisp?Gobo
@Roger, how is that relevant? The point is that it's not readable because I'm not familiar or competent with it, not because it's bad code. The same is true for C/C# code which uses new features, like Generics/anonymous methods in C#2.x, or Lambdas in 3.x, until I take the time to learn them, and use them sufficiently to become fluent with them.Schwa
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! But maintaining consistency within a project is a useful goal. When you break a project's accepted conventions you lead to debate that detracts from solving the real goal. Just look at all the effort spent on this question, versus how long it would take to add parentheses and move on.Gobo
FWIW, I made a conscious decision not to learn the C operator precedence table, on the principal that if I had to learn the table to parse my expressions it would confuse other people (and maybe me, later on). If it isn't obvious, I parenthesize it.Become
So, what constitutes readable, or unreadable, good, or bad, code is dependant on an individual teams' consistency standards, and not on the code itself? I guess you're free to define anything anyway you want, but that's not how I would define a quality attribute. to me, the definition has to be such that it is dependant on characteristics internal to whatever is being evaluated, not on external factors which can differ from one setting to another.Schwa
In this forum we are not on a single team, with a single standard. SO making statements which purport to judge qualities like readability, or clarity, when your personal definition is dependant on an internal standard which others here may or may not subscribe to or use, would be .... not as clear ???Schwa
Charles: It depends on context, of course. How anything is measured depends on the requirements, and those are different for software making my coffee, controlling the space shuttle, or doing my taxes. We are not on a single team, but I can deduce it is neither common nor accepted for his project because he had to ask.Gobo
@Roger, well, his question was about the precedence order, and, contray to your statement about his feelings, to quote him " It would be pretty nice if I cold write it like this: ,<snip> Clear and concise!" That tells me just the opposite of what you are saying. He would prefer to use the ternary! But this is still not relevant to my point which is that software quality be it readability, or clarity, or whatever, does not, should not, depend on subjective judgments. If he said If Else clauses are evil, does that make them so? Of course not, no matter if his team treats them as anathema.Schwa
If his project had a specific convention not to use else clauses, then he should either 1) not use else clauses, or 2) get that convention changed before using else clauses. This is assuming that he wants to solve the problem his project set out to solve---if he just wants to spend all day arguing with other team members, then he should do as you say. We will just have to disagree, because no matter how you judge code, doesn't necessarily make your criteria right for any other project.Gobo
"if he just wants to spend all day arguing with other team members, then he should do as you say.." ???? your chutzpa is astounding... You just assume that your judgment is universal and that anyone who disagrees is just a argumentative iconoclast ? Then you clearly miss my point totally, and after making several attempts now to communicate it, I guess we will have to agree to stop communicating... (I can't say agree to disagree, cause frankly, I have no sense from you that you have any idea what it is I disagree with you about... )Schwa
You get my +1 for quoting Brian W. Kernighan.Course
H
4

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);
Hexangular answered 19/11, 2009 at 14:23 Comment(2)
Re: your last sentence. I note that the question wasn't about order of evaluation, it was about associativity. Those two things are completely different.Weighbridge
I should have been more clear that it associates in a way that allows the expression to be evaluated left to right in the expected fashion.Hexangular
S
2
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.

Samuella answered 17/12, 2010 at 2:11 Comment(1)
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.Samuella
T
1

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."

Tiossem answered 19/11, 2009 at 14:16 Comment(6)
True, but this doesn't say anything about associativity.Greengrocery
@Vilx - You are right, however, the associativity part of the question was added during an edit, after I had already given my answer (refer to the edit history on the question). The original question read: "But it does depend on the ternary operator precedence ". So my answer was based on precedence.Tiossem
Wow. Revisiting old answers? :) Anyway, I'm still hazy myself about the "associativity vs precedence" thing, so - my apologies! :)Greengrocery
Vilx - Actually, I owe you an apology too. I responded because someone downvoted this today, so I thought it was you since yours was the only reponse. But now I realize your response was over 2 years ago. So I guess someone downvoted without providing a reason. Anyway, sorry for the confusion :)Tiossem
Nop, I haven't given you any votes - up or down. But here - have an upvote. Just so. :)Greengrocery
@Vilx - Much obliged, and I really liked your answer to this question: #9054440, so I returned the favor :).Tiossem

© 2022 - 2024 — McMap. All rights reserved.