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) shown in the following example:
((1 + 2) / 3)
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 the order. Binary operators (except for assignment operators) are left-associative; i.e., they are evaluated from left to right. The assignment operators, unary operators, and the conditional operator are right-associative; i.e., they are 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 binary operator that is left-associative. You can insert parentheses to change the default order of evaluation. C# overloads operators, which means the same operator may have 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.
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: = *= /= %= += -= <<= >>= &= ^= |= |
The syntax for the checked and unchecked operators is:
checked (expression) unchecked (expression)
The syntax for the checked and unchecked statements is:
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. For 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 only applies to runtime expressions, since constant expressions are checked during compilation (though this can be turned off with the /checked [+|-] command-line switch). The unchecked operator disables arithmetic checking at compile time, and is seldom useful, but does make expressions such as the following compile:
const int signedBit = unchecked((int)0x80000000);