107

What are the benefits and drawbacks of the ?: operator as opposed to the standard if-else statement. The obvious ones being:

Conditional ?: Operator

  • Shorter and more concise when dealing with direct value comparisons and assignments
  • Doesn't seem to be as flexible as the if/else construct

Standard If/Else

  • Can be applied to more situations (such as function calls)
  • Often are unnecessarily long

Readability seems to vary for each depending on the statement. For a little while after first being exposed to the ?: operator, it took me some time to digest exactly how it worked. Would you recommend using it wherever possible, or sticking to if/else given that I work with many non-programmers?

Garrett
  • 4,007
  • 2
  • 41
  • 59
KChaloux
  • 3,918
  • 6
  • 37
  • 52
  • 8
    You already got the gist of it. – Byron Whitlock Jul 22 '10 at 19:51
  • I've only done a little in C#, but most languages allow function calls inside the conditional operator. Does C# really not? That seems very strange... – Nicholas Knight Jul 22 '10 at 19:51
  • 1
    @Nicholas Knight: I'm guessing the OP means that you can't do, e.g., `SomeCheck() ? DoFirstThing() : DoSecondThing();` -- you have to use the expression to return a value. – Dan Tao Jul 22 '10 at 19:53
  • 6
    Use it _where it is clear_, stick with if/else if it isn't. Code clarity should be your main consideration. – hollsk Jul 22 '10 at 19:53
  • 8
    Have you seen '??' yet? Seriously, if you think ternaries are cool... – pdr Jul 22 '10 at 19:55
  • Similar question regarding the speed difference between the two http://stackoverflow.com/questions/2586842/javascript-if-else-or-ternary-operator-is-faster (It's negligible). – Gordon Jul 22 '10 at 20:02
  • @pdr, do you have a link? I can't google that.... – Byron Whitlock Jul 22 '10 at 20:05
  • @Gordon: I actually got curious about that yesterday and ran two loops that compared 2^16 randomly generated integers with if/else and ?:. I came to the same conclusion: it doesn't make a noticeable difference on performance. – KChaloux Jul 22 '10 at 20:10
  • 4
    +1 for not calling it simply "the ternary operator" as many do. Even though it is the only ternary (as opposed to unary and binary) operator in C#, that's not its name. – John M Gant Jul 22 '10 at 20:43
  • @Byron, see: http://msdn.microsoft.com/en-us/library/ms173224(VS.90).aspx `??` is essentially a 'default if null' operator, or technically a "null-coalescing" operator. – Nathan Ernst Jul 22 '10 at 22:43

17 Answers17

125

I would basically recommend using it only when the resulting statement is extremely short and represents a significant increase in conciseness over the if/else equivalent without sacrificing readability.

Good example:

int result = Check() ? 1 : 0;

Bad example:

int result = FirstCheck() ? 1 : SecondCheck() ? 1 : ThirdCheck() ? 1 : 0;
Dan Tao
  • 125,917
  • 54
  • 300
  • 447
  • 5
    Good call, but for the record, that's "concision." – mqp Jul 22 '10 at 19:59
  • 6
    @mquander, you sure about that? http://www.merriam-webster.com/dictionary/concise – Byron Whitlock Jul 22 '10 at 20:07
  • 1
    Hmm, go figure; perhaps it can just as well be either. – mqp Jul 22 '10 at 20:11
  • 1
    @mquander: I figured that was a clever joke. If so, kudos ;) Or should I say, "kds." – Dan Tao Jul 22 '10 at 20:24
  • 40
    I always start out with a simple one and make it more complex over time until it is completely unreadable. – Jouke van der Maas Jul 22 '10 at 20:39
  • 9
    Readability in the second example could easily be rectified with better formatting. But, as the OP is recommending, it comes down to readability and terseness versus verbosity. – Nathan Ernst Jul 22 '10 at 22:41
  • 5
    Not part of the OP's question, but important to note is the fact that you cannot have a `return` be part of the result of the ternary operation. For example: `check() ? return 1 : return 0;` will not work, but `return check() ? 1 : 0;` will. Always fun to find these little quirks in programming. – CSS Feb 29 '16 at 21:49
53

This is pretty much covered by the other answers, but "it's an expression" doesn't really explain why that is so useful...

In languages like C++ and C#, you can define local readonly fields (within a method body) using them. This is not possible with a conventional if/then statement because the value of a readonly field has to be assigned within that single statement:

readonly int speed = (shiftKeyDown) ? 10 : 1;

is not the same as:

readonly int speed;  
if (shifKeyDown)  
    speed = 10;    // error - can't assign to a readonly
else  
    speed = 1;     // error  

In a similar way you can embed a tertiary expression in other code. As well as making the source code more compact (and in some cases more readable as a result) it can also make the generated machine code more compact and efficient:

MoveCar((shiftKeyDown) ? 10 : 1);

...may generate less code than having to call the same method twice:

if (shiftKeyDown)
    MoveCar(10);
else
    MoveCar(1);

Of course, it's also a more convenient and concise form (less typing, less repetition, and can reduce the chance of errors if you have to duplicate chunks of code in an if/else). In clean "common pattern" cases like this:

object thing = (reference == null) ? null : reference.Thing;

... it is simply faster to read/parse/understand (once you're used to it) than the long-winded if/else equivalent, so it can help you to 'grok' code faster.

Of course, just because it is useful does not mean it is the best thing to use in every case. I'd advise only using it for short bits of code where the meaning is clear (or made more clear) by using ?: - if you use it in more complex code, or nest ternary operators within each other it can make code horribly difficult to read.

mark_h
  • 5,233
  • 4
  • 36
  • 52
Jason Williams
  • 56,972
  • 11
  • 108
  • 137
  • @JaminGrey _"that doesn't mean that, when the constant is created, it is set to either 10 or 1."_ Do you mean _does_ mean that? Incorrect comments may cause more confusion to new C++ programmers than the issue you were attempting to clear up ;) – Clonkex Nov 07 '13 at 03:02
  • 5
    For future readers coming across this, by "*const int speed = (shiftKeyDown) ? 10 : 1;*", that means that when the constant *is first created*, it is set to either 10 or 1. It **doesn't** mean that each time the constant is accessed it does a check. (Just incase a newer C++ programmer was confused) – Jamin Grey Nov 07 '13 at 04:39
  • 2
    ...or to put it another way, a `const` is constant, i.e. it cannot be changed after the statement in which it is declared has executed. – Jason Williams Nov 07 '13 at 07:51
  • 1
    @JaminGrey. Shouldn't it be `readonly` though? I always thought `const` meant "*resolved at compile time and in-lined wherever used*". – Nolonar Jul 24 '14 at 11:33
  • My answer is primarily about const _locals_ within methods, rather than const _fields_. The crux of it is that you can't use two separate statements (if..else) to initialise a const, so to choose between two initial values you _must_ use `?:`. For a readonly, you can use if..else to initialise it within the constructor (although you _could_ use `?:`, you don't _need_ to use it). – Jason Williams Jul 24 '14 at 14:00
  • I personally feel that both ways above are quite unreadable. What is 10 and 1? Speed or distance? It is helpful to know what is going into a method just as much as the value. So I would change it up to this, which removes the if/else and (imho) increases readability no end. `var distance = (shiftKeyDown ? 10 : 1); MoveCar(distance);` – Colin Wiseman Jul 25 '14 at 21:18
  • 1
    @ColinWiseman, it's an _example_ to illustrate how ?: _can_ be used. I specifically state that just because you can do it, it does not mean that it is necessarily the "best" thing to do in any specific case. To work that out, the reader is expected to use their brain each time they come across a case where it might be useful to them. – Jason Williams Jul 27 '14 at 16:16
14

I usually choose a ternary operator when I'd have a lot of duplicate code otherwise.

if (a > 0)
    answer = compute(a, b, c, d, e);
else
    answer = compute(-a, b, c, d, e);

With a ternary operator, this could be accomplished with the following.

answer = compute(a > 0 ? a : -a, b, c, d, e); 
  • 12
    personally I would do `aVal = a > 0 ? a : -a; answer = compute(aVal,b,c,d,e);` Especially if `b`, `c`, `d` and `e` required treatment too. – corsiKa Jul 22 '10 at 21:56
  • 10
    Why use a conditional at all in this example? Just get Abs(a) and call compute() once. – Ash Jul 23 '10 at 05:32
  • 2
    Yeah, I didn't create the best example. :) – Ryan Bright Jul 27 '10 at 18:33
  • To a newbie, that does not look equivalent. Wouldn't it need to be answer = compute(a > 0 ? a, b, c, d, e : -a, b, c, d, e); ? – pbreitenbach Oct 20 '10 at 23:25
  • @pbreitenbach: no - it's a matter of precedence - the first argument to `compute(...)` is `a > 0 ? a : -1`, which is all evaluated separately from the other comma-separated arguments. Anyway, unfortunately C++ lacks the notation your question posits for handling "tuples" of comma-separated values, so even `a > 0 ? (a, b, c, d, e) : (-a, b, c, d, e)` is illegal, and there's nothing very similar that works without changes to `compute` itself. – Tony Delroy Nov 26 '13 at 01:14
12

I find it particularly helpful when doing web development if I want to set a variable to a value sent in the request if it is defined or to some default value if it is not.

wshato
  • 513
  • 3
  • 16
11

A really cool usage is:

x = foo ? 1 :
    bar ? 2 :
    baz ? 3 :
          4;
aib
  • 45,516
  • 10
  • 73
  • 79
  • 10
    Be careful with this in PHP, the ternary operator associates the wrong way in PHP. Essentially, if `foo` is false, then the whole thing will evaluate to 4 without doing the other tests. – Tom Busby Mar 21 '15 at 20:16
7

Sometimes it can make the assignment of a bool value easier to read at first glance:

// With
button.IsEnabled = someControl.HasError ? false : true;

// Without
button.IsEnabled = !someControl.HasError;
Tyler Pantuso
  • 835
  • 8
  • 16
6

I'd recommend limiting the use of the ternary(?:) operator to simple single line assignment if/else logic. Something resembling this pattern:

if(<boolCondition>) {
    <variable> = <value>;
}
else {
    <variable> = <anotherValue>;
}

Could be easily converted to:

<variable> = <boolCondition> ? <value> : <anotherValue>;

I would avoid using the ternary operator in situations that require if/else if/else, nested if/else, or if/else branch logic that results in the evaluation of multiple lines. Applying the ternary operator in these situations would likely result in unreadable, confusing, and unmanageable code. Hope this helps.

HOCA
  • 1,073
  • 2
  • 9
  • 20
6

The conditional operator is great for short conditions, like this:

varA = boolB ? valC : valD;

I use it occasionally because it takes less time to write something that way... unfortunately, this branching can sometimes be missed by another developer browsing over your code. Plus, code isn't usually that short, so I usually help readability by putting the ? and : on separate lines, like this:

doSomeStuffToSomething(shouldSomethingBeDone()
    ? getTheThingThatNeedsStuffDone()
    : getTheOtherThingThatNeedsStuffDone());

However, the big advantage to using if/else blocks (and why I prefer them) is that it's easier to come in later and add some additional logic to the branch,

if (shouldSomethingBeDone()) {
    doSomeStuffToSomething(getTheThingThatNeedsStuffDone());
    doSomeAdditionalStuff();
} else {
doSomeStuffToSomething(getTheOtherThingThatNeedsStuffDone());
}

or add another condition:

if (shouldSomethingBeDone()) {
    doSomeStuffToSomething(getTheThingThatNeedsStuffDone());
    doSomeAdditionalStuff();
} else if (shouldThisOtherThingBeDone()){
    doSomeStuffToSomething(getTheOtherThingThatNeedsStuffDone());
}

So, in the end, it's about convenience for you now (shorter to use :?) vs. convenience for you (and others) later. It's a judgment call... but like all other code-formatting issues, the only real rule is to be consistent, and be visually courteous to those who have to maintain (or grade!) your code.

(all code eye-compiled)

iandisme
  • 6,346
  • 6
  • 44
  • 63
5

One thing to recognize when using the ternary operator that it is an expression not a statement.

In functional languages like scheme the distinction doesn't exists:

(if (> a b) a b)

Conditional ?: Operator "Doesn't seem to be as flexible as the if/else construct"

In functional languages it is.

When programming in imperative languages I apply the ternary operator in situations where I typically would use expressions (assignment, conditional statements, etc).

Ken Struys
  • 1,789
  • 1
  • 10
  • 17
5

While the above answers are valid, and I agree with readability being important, there are 2 further points to consider:

  1. In C#6, you can have expression-bodied methods.

This makes it particularly concise to use the ternary:

string GetDrink(DayOfWeek day) 
   => day == DayOfWeek.Friday
      ? "Beer" : "Tea";
  1. Behaviour differs when it comes to implicit type conversion.

If you have types T1 and T2 that can both be implicitly converted to T, then the below does not work:

T GetT() => true ? new T1() : new T2();

(because the compiler tries to determine the type of the ternary expression, and there is no conversion between T1 and T2.)

On the other hand, the if/else version below does work:

T GetT()
{
   if (true) return new T1();
   return new T2();
}

because T1 is converted to T and so is T2

la-yumba
  • 81
  • 1
  • 4
4

If I'm setting a value and I know it will always be one line of code to do so, I typically use the ternary (conditional) operator. If there's a chance my code and logic will change in the future, I use an if/else as it's more clear to other programmers.

Of further interest to you may be the ?? operator.

drharris
  • 11,194
  • 5
  • 43
  • 56
4

The advantage of the conditional operator is that it is an operator. In other words, it returns a value. Since if is a statement, it cannot return a value.

Gabe
  • 84,912
  • 12
  • 139
  • 238
2

There is some performance benefit of using the the ? operator in eg. MS Visual C++, but this is a really a compiler specific thing. The compiler can actually optimize out the conditional branch in some cases.

darklon
  • 468
  • 3
  • 13
2

The scenario I most find myself using it is for defaulting values and especially in returns

return someIndex < maxIndex ? someIndex : maxIndex;

Those are really the only places I find it nice, but for them I do.

Though if you're looking for a boolean this might sometimes look like an appropriate thing to do:

bool hey = whatever < whatever_else ? true : false;

Because it's so easy to read and understand, but that idea should always be tossed for the more obvious:

bool hey = (whatever < whatever_else);
Jimmy Hoffa
  • 5,909
  • 30
  • 53
2

If you need multiple branches on the same condition, use an if:

if (A == 6)
  f(1, 2, 3);
else
  f(4, 5, 6);

If you need multiple branches with different conditions, then if statement count would snowball, you'll want to use the ternary:

f( (A == 6)? 1: 4, (B == 6)? 2: 5, (C == 6)? 3: 6 );

Also, you can use the ternary operator in initialization.

const int i = (A == 6)? 1 : 4;

Doing that with if is very messy:

int i_temp;
if (A == 6)
   i_temp = 1;
else
   i_temp = 4;
const int i = i_temp;

You can't put the initialization inside the if/else, because it changes the scope. But references and const variables can only be bound at initialization.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
2

The ternary operator can be included within an rvalue, whereas an if-then-else cannot; on the other hand, an if-then-else can execute loops and other statements, whereas the ternary operator can only execute (possibly void) rvalues.

On a related note, the && and || operators allow some execution patterns which are harder to implement with if-then-else. For example, if one has several functions to call and wishes to execute a piece of code if any of them fail, it can be done nicely using the && operator. Doing it without that operator will either require redundant code, a goto, or an extra flag variable.

supercat
  • 77,689
  • 9
  • 166
  • 211
2

With C# 7, you can use the new ref locals feature to simplify the conditional assignment of ref-compatible variables. So now, not only can you do:

int i = 0;

T b = default(T), c = default(T);

// initialization of C#7 'ref-local' variable using a conditional r-value⁽¹⁾

ref T a = ref (i == 0 ? ref b : ref c);

...but also the extremely wonderful:

// assignment of l-value⁽²⁾ conditioned by C#7 'ref-locals'

(i == 0 ? ref b : ref c) = a;

That line of code assigns the value of a to either b or c, depending on the value of i.



Notes
1. r-value is the right-hand side of an assignment, the value that gets assigned.
2. l-value is the left-hand side of an assignment, the variable that receives the assigned value.
Glenn Slayden
  • 17,543
  • 3
  • 114
  • 108