Operator Precedence

The compiler must know the order in which to evaluate a series of operators. For example, if you write:

myVariable = 5 + 7 * 3;

there are three operators for the compiler to evaluate (=, +, and *). It could, for example, operate left to right, which would assign the value 5 to myVariable, then add 7 to the 5 (12) and multiply by 3 (36)—but of course, then it would throw that 36 away. This is clearly not what is intended.

The rules of precedence tell the compiler which operators to evaluate first. As is the case in algebra, multiplication has higher precedence than addition, so 5+7 x 3 is equal to 26 rather than 36. Both multiplication and addition have higher precedence than assignment, so the compiler will do the math and then assign the result (26) to myVariable only after the math is completed.

In C#, you can also use parentheses to change the order of precedence much as you would in algebra. Thus, you can change the result by writing:

myVariable = (5+7) * 3;

Grouping the elements of the assignment in this way causes the compiler to add 5+7, multiply the result by 3, and then assign that value (36) to myVariable.

Table 4-3 summarizes operator precedence in C#, using x and y as possible terms to be operated upon.[1]

Table 4-3. Operator precedence

Category

Operators

Primary

(x) x.y x->y f(x) a[x] x++ x-- new typeof sizeof checked unchecked stackalloc

Unary

+ - ! ~ ++x --x (T)x *x &x

Multiplicative

* / %

Additive

+ -

Shift

<< >>

Relational

< > <= >= is as

Equality

== !=

Logical (bitwise) AND

&

Logical (bitwise) XOR

^

Logical (bitwise) OR

|

Conditional AND

&&

Conditional OR

||

Conditional

?:

Assignment

= *= /= %= += -= <<= >>= &= ^= |=

The operators are listed in precedence order according to the category in which they fit. That is, the primary operators (such as x++) are evaluated before the unary operators (such as !). Multiplication is evaluated before addition.

There are a lot of operators in this table, and you don’t need to memorize their order of precedence. It never hurts to use parentheses if you’re not sure of the exact order. The compiler doesn’t mind if you use them where they’re not needed, and it may help to make the operation clearer to readers of your code.

In some complex equations, you might need to nest parentheses to ensure the proper order of operations. For example, say you want to know how many seconds a hypothetical family wastes each morning. The adults spend 20 minutes over coffee each morning and 10 minutes reading the newspaper. The children waste 30 minutes dawdling and 10 minutes arguing.

Here’s the algorithm:

(((minDrinkingCoffee + minReadingNewspaper )* numAdults ) +
((minDawdling + minArguing) * numChildren)) * secondsPerMinute;

Tip

An algorithm is a well-defined series of steps to accomplish a task.

Although this works, it is hard to read and hard to get right. It’s much easier to use interim variables:

wastedByEachAdult = minDrinkingCoffee + minReadingNewspaper;
wastedByAllAdults = wastedByEachAdult * numAdults;
wastedByEachKid = minDawdling + minArguing;
wastedByAllKids = wastedByEachKid * numChildren;
wastedByFamily = wastedByAllAdults + wastedByAllKids;
totalSeconds = wastedByFamily * 60;

The latter example uses many more interim variables, but it is far easier to read, understand, and (most importantly) debug. As you step through this program in your debugger, you can see the interim values and make sure they are correct. See Chapter 9 for more information.



[1] This table includes operators that are beyond the scope of this book. For a fuller explanation of each, please see Programming C#, Fifth Edition, by Jesse Liberty and Donald Xie (O’Reilly).

Get Learning C# 3.0 now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.