only for RuBoard - do not distribute or recompile Previous Section Next Section

2.4 Expressions and Operators

An expression is a sequence of operators and operands that specifies a computation. C# has unary operators, binary operators, and one ternary operator. Complex expressions can be built because an operand may itself be an expression, such as the operand (1 + 2) in the following example:

((1 + 2) / 3)

2.4.1 Operator Precedence

When an expression contains multiple operators, the precedence of the operators controls the order in which the individual operators are evaluated. When the operators are of the same precedence, their associativity determines their order of evaluation. Binary operators (except for assignment operators) are left-associative and are evaluated from left to right. The assignment operators, unary operators, and the conditional operator are right-associative, evaluated from right to left.

For example:

1 + 2 + 3 * 4

is evaluated as:

((1 + 2) + (3 * 4))

because * has a higher precedence than +, and + is a left-associative binary operator. You can insert parentheses to change the default order of evaluation. C# also overloads operators, which means the same operator symbols can have different meanings in different contexts (e.g., primary, unary, etc.) or different meanings for different types.

Table 2-2 lists C#'s operators in order of precedence. Operators in the same box have the same precedence, and operators in italic may be overloaded for custom types (see Section 2.9.8 later in this chapter).

Table 2-2. Operator Precedence Table

Category

Operators

Primary

Grouping:(x)
Member access: x.y
Struct pointer member access: ->
Method call: f(x)
Indexing: a[x]
Post increment: x++
Post decrement:x--
Constructor call: new
Array stack allocation: stackalloc
Type retrieval: typeof
Struct size retrieval: sizeof
Arithmetic check on: checked
Arithmetic check off: unchecked

Unary

Positive value of (passive):
+Negative value of:
-Not:!
Bitwise complement: ~
Pre increment: ++x
Pre decrement:-- x
Type cast: (T)x
Value at address: *
Address of value: &

Multiplicative

Multiply: *
Divide: /
Division remainder: %

Additive

Add: +
Subtract: -

Shift

Shift bits left: <<
Shift bits right:>>

Relational

Less than: <
Greater than: >
Less than or equal to:<=
Greater than or equal to: >=
Type equality/compatibility: is
Conditional type conversion: as

Equality

Equals: ==
Not equals: !=

Logical bitwise

And: &

 

Exclusive or: ^

 

Or: |

Logical Boolean

And: &&

 

Or: ||

 

Ternary conditional: ?: e.g. int x = a > b ? 2 : 7; is equivalent to :int x; if (a > b) x = 2; else x = 7;

Assignment

Assign/modify: = *= /= %= += -= <<= >>= &= ^= |=

2.4.2 Arithmetic Overflow Check Operators

Checked/unchecked operator syntax:

checked (expression)
unchecked (expression)

Checked/unchecked statement syntax:

checked statement-or-statement-block
unchecked statement-or-statement-block

The checked operator tells the runtime to generate an OverflowException if an integral expression exceeds the arithmetic limits of that type. The checked operator affects expressions with the ++, --, (unary)-, +, -, *, /, and explicit conversion operators ( ) between integral types (see Section 2.2.5.1 later in this chapter). Here's an example:

int a = 1000000;
int b = 1000000;

// Check an expression
int c = checked(a*b);

// Check every expression in a statement block
checked {
   ...
   c = a * b;
   ...
}

The checked operator applies only to runtime expressions, because constant expressions are checked during compilation (though this can be turned off with the /checked [+|-] command-line compiler switch). The unchecked operator disables arithmetic checking at compile time and is seldom useful, but it can enable expressions such as this to compile:

const int signedBit = unchecked((int)0x80000000);
only for RuBoard - do not distribute or recompile Previous Section Next Section