CATEGORII DOCUMENTE |
Expressions are constructed from operands and operators. The operators of an expression indicate which operations to apply to the operands. Examples of operators include +, -, *, /, and new. Examples of operands include literals, fields, local variables, and expressions.
There are three types of operators:
Unary operators. The unary operators take one operand and use either prefix notation (such as -x) or postfix notation (such as x++).
Binary operators. The binary operators take two operands and all use infix notation (such as x + y).
Ternary operator. Only one ternary operator, ?:, exists. The ternary operator takes three operands and uses infix notation (c? x: y).
The order of evaluation of operators in an expression is determined by the precedence and associativity of the operators (7.2.1).
Operands in an expression are evaluated from left to right. For example, in F(i) + G(i++) * H(i), method F is called using the old value of i, then method G is called with the old value of i, and, finally, method H is called with the new value of i. This is separate from and unrelated to operator precedence.
Certain operators can be overloaded. Operator overloading permits user-defined operator implementations to be specified for operations where one or both of the operands are of a user-defined class or struct type (7.2.2).
When an expression contains multiple operators, the precedence of the operators control the order in which the individual operators are evaluated. For example, the expression x + y * z is evaluated as x + (y * z) because the * operator has higher precedence than the + operator. The precedence of an operator is established by the definition of its associated grammar production. For example, an additive-expression consists of a sequence of multiplicative-expressions separated by + or - operators, thus giving the + and - operators lower precedence than the *, /, and % operators.
The following table summarizes all operators in order of precedence from highest to lowest:
Section |
Category |
Operators |
7.5 |
Primary |
x.y f(x) a[x] x++ x-- new typeof checked unchecked |
7.6 |
Unary |
+ - ! ~ ++x --x (T)x |
7.7 |
Multiplicative |
* / % |
7.7 |
Additive |
+ - |
7.8 |
Shift |
<< >> |
7.9 |
Relational and type testing |
< > <= >= is as |
7.9 |
Equality |
== != |
7.10 |
Logical AND |
& |
7.10 |
Logical XOR |
^ |
7.10 |
Logical OR |
| |
7.11 |
Conditional AND |
&& |
7.11 |
Conditional OR |
|| |
7.12 |
Conditional |
?: |
7.13 |
Assignment |
= *= /= %= += -= <<= >>= &= ^= |= |
When an operand occurs between two operators with the same precedence, the associativity of the operators controls the order in which the operations are performed:
Except for the assignment operators, all binary operators are left-associative, meaning that operations are performed from left to right. For example, x + y + z is evaluated as (x + y) + z.
The assignment operators and the conditional operator (?:) are right-associative, meaning that operations are performed from right to left. For example, x = y = z is evaluated as x = (y = z).
Precedence and associativity can be controlled using parentheses. For example, x + y * z first multiplies y by z and then adds the result to x, but (x + y) * z first adds x and y and then multiplies the result by z.
All unary and binary operators have predefined implementations that are automatically available in any expression. In addition to the predefined implementations, user-defined implementations can be introduced by including operator declarations in classes and structs (10.9). User-defined operator implementations always take precedence over predefined operator implementations: Only when no applicable user-defined operator implementations exist will the predefined operator implementations be considered.
The overloadable unary operators are:
+ - ! ~ ++ -- true false
The overloadable binary operators are:
+ - * / % & | ^ << >> == != > < >= <=
Only the operators listed above can be overloaded. In particular, it is not possible to overload member access, method invocation, or the =, &&, ||, ?:, new, typeof, is, as, checked, and unchecked operators.
When a binary operator is overloaded, the corresponding assignment operator (if any) is also implicitly overloaded. For example, an overload of operator * is also an overload of operator *=. This is described further in 7.13. Note that the assignment operator itself (=) cannot be overloaded. An assignment always performs a simple bit-wise copy of a value into a variable.
Cast operations, such as (T)x, are overloaded by providing user-defined conversions (6.4).
Element access, such as a[x], is not considered an overloadable operator. Instead, user-defined indexing is supported through indexers (10.8).
In expressions, operators are referenced using operator notation, and in declarations, operators are referenced using functional notation. The following table shows the relationship between operator and functional notations for unary and binary operators. In the first entry, op denotes any overloadable unary prefix operator. In the second entry, op denotes the unary postfix ++ and -- operators. In the third entry, op denotes any overloadable binary operator.
Operator notation |
Functional notation |
op x |
operator op(x) |
x op |
operator op(x) |
x op y |
operator op(x, y) |
User-defined operator declarations always require at least one of the parameters to be of the class or struct type that contains the operator declaration. Thus, it is not possible for a user-defined operator to have the same signature as a predefined operator.
User-defined operator declarations cannot modify the syntax, precedence, or associativity of an operator. For example, the * operator is always a binary operator, always has the precedence level specified in 7.2.1, and is always left-associative.
While it is possible for a user-defined operator to perform any computation it pleases, implementations that produce results other than those that are intuitively expected are strongly discouraged. For example, an implementation of operator == should compare the two operands for equality and return an appropriate result.
The descriptions of individual operators in 7.5 through 7.13 specify the predefined implementations of the operators and any additional rules that apply to each operator. The descriptions make use of the terms unary operator overload resolution, binary operator overload resolution, and numeric promotion, definitions of which are found in the following sections.
An operation of the form op x or x op, where op is an overloadable unary operator, and x is an expression of type X, is processed as follows:
The set of candidate user-defined operators provided by X for the operation operator op(x) is determined using the rules of 7.2.5.
If the set is not empty, then this becomes the set of candidate operators for the operation. Otherwise, the predefined unary operator op implementations become the set of candidate operators for the operation. The predefined implementations of a given operator are specified in the description of the operator (7.5 and 7.6).
The overload resolution rules of 7.4.2 are applied to the set of candidate operators to select the best operator with respect to the argument list (x), and this operator becomes the result of the overload resolution process. If overload resolution fails to select a single best operator, an error occurs.
An operation of the form x op y, where op is an overloadable binary operator, x is an expression of type X, and y is an expression of type Y, is processed as follows:
The set of candidate user-defined operators provided by X and Y for the operation operator op(x, y) is determined. The set consists of the union of the candidate operators provided by X and the candidate operators provided by Y, each determined using the rules of 7.2.5. If X and Y are the same type, or if X and Y are derived from a common base type, then shared candidate operators only occur in the combined set once.
If the set is not empty, then this becomes the set of candidate operators for the operation. Otherwise, the predefined binary operator op implementations become the set of candidate operators for the operation. The predefined implementations of a given operator are specified in the description of the operator (7.7 through 7.13).
The overload resolution rules of 7.4.2 are applied to the set of candidate operators to select the best operator with respect to the argument list (x, y), and this operator becomes the result of the overload resolution process. If overload resolution fails to select a single best operator, an error occurs.
Given a type T and an operation operator op(A), where op is an overloadable operator and A is an argument list, the set of candidate user-defined operators provided by T for operator op(A) is determined as follows:
For all operator op declarations in T, if at least one operator is applicable (7.4.2.1) with respect to the argument list A, then the set of candidate operators consists of all applicable operator op declarations in T.
Otherwise, if T is object, the set of candidate operators is empty.
Otherwise, the set of candidate operators provided by T is the set of candidate operators provided by the direct base class of T.
Numeric promotion consists of automatically performing certain implicit conversions of the operands of the predefined unary and binary numeric operators. Numeric promotion is not a distinct mechanism, but rather an effect of applying overload resolution to the predefined operators. Numeric promotion specifically does not affect evaluation of user-defined operators, although user-defined operators can be implemented to exhibit similar effects.
As an example of numeric promotion, consider the predefined implementations of the binary * operator:
int operator *(int x, int y);
uint operator *(uint x, uint y);
long operator *(long x, long y);
ulong operator *(ulong x, ulong y);
float operator *(float x, float y);
double operator *(double x, double y);
decimal operator *(decimal x, decimal y);
When overload resolution rules (7.4.2) are applied to this set of operators, the effect is to select the first of the operators for which implicit conversions exist from the operand types. For example, for the operation b * s, where b is a byte and s is a short, overload resolution selects operator *(int, int) as the best operator. Thus, the effect is that b and s are converted to int, and the type of the result is int. Likewise, for the operation i * d, where i is an int and d is a double, overload resolution selects operator *(double, double) as the best operator.
Unary numeric promotion occurs for the operands of the predefined +, -, and ~ unary operators. Unary numeric promotion simply consists of converting operands of type sbyte, byte, short, ushort, or char to type int. Additionally, for the unary - operator, unary numeric promotion converts operands of type uint to type long.
Binary numeric promotion occurs for the operands of the predefined +, -, *, /, %, &, |, ^, ==, !=, >, <, >=, and <= binary operators. Binary numeric promotion implicitly converts both operands to a common type which, in case of the non-relational operators, also becomes the result type of the operation. Binary numeric promotion consists of applying the following rules, in the order they appear here:
If either operand is of type decimal, the other operand is converted to type decimal, or an error occurs if the other operand is of type float or double.
Otherwise, if either operand is of type double, the other operand is converted to type double.
Otherwise, if either operand is of type float, the other operand is converted to type float.
Otherwise, if either operand is of type ulong, the other operand is converted to type ulong, or an error occurs if the other operand is of type sbyte, short, int, or long.
Otherwise, if either operand is of type long, the other operand is converted to type long.
Otherwise, if either operand is of type uint and the other operand is of type sbyte, short, or int, both operands are converted to type long.
Otherwise, if either operand is of type uint, the other operand is converted to type uint.
Otherwise, both operands are converted to type int.
Note that the first rule disallows any operations that mix the decimal type with the double and float types. The rule follows from the fact that there are no implicit conversions between the decimal type and the double and float types.
Also note that it is not possible for an operand to be of type ulong when the other operand is of a signed integral type. The reason is that no integral type exists that can represent the full range of ulong as well as the signed integral types.
In both of the above cases, a cast expression can be used to explicitly convert one operand to a type that is compatible with the other operand.
In the example
decimal AddPercent(decimal x, double percent)
a compile-time error occurs because a decimal cannot be multiplied by a double. The error is resolved by explicitly converting the second operand to decimal:
decimal AddPercent(decimal x,
double percent) {
return x * (decimal)(1.0 +
percent / 100.0);
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 928
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved