[ Team LiB ] |
3.6 OperatorsAn operator is a symbol that causes C# to take an action. The C# primitive types (e.g., int) support a number of operators such as assignment, increment, and so forth. Their use is highly intuitive, with the possible exception of the assignment operator (=) and the equality operator (==), which are often confused. 3.6.1 The Assignment Operator (=)Section 3.3, earlier in this chapter, demonstrates the use of the assignment operator. This symbol causes the operand on the left side of the operator to have its value changed to whatever is on the right side of the operator. 3.6.2 Mathematical OperatorsC# uses five mathematical operators: four for standard calculations and a fifth to return the remainder in integer division. The following sections consider the use of these operators. 3.6.2.1 Simple arithmetical operators (+, -, *, /)C# offers operators for simple arithmetic: the addition (+), subtraction (-), multiplication (*), and division (/) operators work as you might expect, with the possible exception of integer division. When you divide two integers, C# divides like a child in fourth grade: it throws away any fractional remainder. Thus, dividing 17 by 4 will return the value 4 (17/4 = 4, with a remainder of 1). C# provides a special operator (modulus, %, which is described in the next section) to retrieve the remainder. Note, however, that C# does return fractional answers when you divide floats, doubles, and decimals. 3.6.2.2 The modulus operator (%) to return remaindersTo find the remainder in integer division, use the modulus operator (%). For example, the statement 17%4 returns 1 (the remainder after integer division). The modulus operator turns out to be more useful than you might at first imagine. When you perform modulus n on a number that is a multiple of n, the result is zero. Thus 80%10 = 0 because 80 is an even multiple of 10. This fact allows you to set up loops in which you take an action every nth time through the loop, by testing a counter to see if %n is equal to zero. This strategy comes in handy in the use of the for loop, as described earlier in this chapter. The effects of division on integers, floats, doubles, and decimals is illustrated in Example 3-16. Example 3-16. Division and modulususing System; class Values { static void Main( ) { int i1, i2; float f1, f2; double d1, d2; decimal dec1, dec2; i1 = 17; i2 = 4; f1 = 17f; f2 = 4f; d1 = 17; d2 = 4; dec1 = 17; dec2 = 4; Console.WriteLine("Integer:\t{0}\nfloat:\t\t{1}", i1/i2, f1/f2); Console.WriteLine("double:\t\t{0}\ndecimal:\t{1}", d1/d2, dec1/dec2); Console.WriteLine("\nModulus:\t{0}", i1%i2); } } Output: Integer: 4 float: 4.25 double: 4.25 decimal: 4.25 Modulus: 1 Now consider this line from Example 3-16: Console.WriteLine("Integer:\t{0}\nfloat:\t\t{1}\n", i1/i2, f1/f2); It begins with a call to Console.Writeline( ), passing in this partial string: "Integer:\t{0}\n This will print the characters Integer:, followed by a tab (\t), followed by the first parameter ({0}), followed by a newline character (\n). The next string snippet: float:\t\t{1}\n is very similar. It prints float:, followed by two tabs (to ensure alignment), the contents of the second parameter ({1}), and then another newline. Notice the subsequent line, as well: Console.WriteLine("\nModulus:\t{0}", i1%i2); This time the string begins with a newline character, which causes a line to be skipped just before the string Modulus: is printed. You can see this effect in the output. 3.6.3 Increment and Decrement OperatorsA common requirement is to add a value to a variable, subtract a value from a variable, or otherwise change the mathematical value, and then to assign that new value back to the same variable. You might even want to assign the result to another variable altogether. The following two sections discuss these cases respectively. 3.6.3.1 Calculate and reassign operatorsSuppose you want to increment the mySalary variable by 5000. You can do this by writing: mySalary = mySalary + 5000; The addition happens before the assignment, and it is perfectly legal to assign the result back to the original variable. Thus, after this operation completes, mySalary will have been incremented by 5000. You can perform this kind of assignment with any mathematical operator: mySalary = mySalary * 5000; mySalary = mySalary - 5000; and so forth. The need to increment and decrement variables is so common that C# includes special operators for self-assignment. Among these operators are +=, -=, *=, /=, and %=, which, respectively, combine addition, subtraction, multiplication, division, and modulus with self-assignment. Thus, you can alternatively write the previous examples as: mySalary += 5000; mySalary *= 5000; mySalary -= 5000; The effect of this is to increment mySalary by 5000, multiply mySalary by 5000, and subtract 5000 from the mySalary variable, respectively. Because incrementing and decrementing by 1 is a very common need, C# (like C and C++ before it) also provides two special operators. To increment by 1, use the ++ operator, and to decrement by 1, use the -- operator. Thus, if you want to increment the variable myAge by 1 you can write: myAge++; 3.6.3.2 The prefix and postfix operatorsTo complicate matters further, you might want to increment a variable and assign the results to a second variable: firstValue = secondValue++; The question arises: do you want to assign before you increment the value, or after? In other words, if secondValue starts out with the value 10, do you want to end with both firstValue and secondValue equal to 11, or do you want firstValue to be equal to 10 (the original value) and secondValue to be equal to 11? C# (again, like C and C++) offer two flavors of the increment and decrement operators: prefix and postfix. Thus you can write: firstValue = secondValue++; // postfix which will assign first, and then increment (firstValue=10, secondValue=11). You can also write: firstValue = ++secondValue; // prefix which will increment first, and then assign (firstValue=11, secondValue=11). It is important to understand the different effects of prefix and postfix, as illustrated in Example 3-17. Example 3-17. Prefix versus postfix incrementusing System; class Values { static void Main( ) { int valueOne = 10; int valueTwo; valueTwo = valueOne++; Console.WriteLine("After postfix: {0}, {1}", valueOne, valueTwo); valueOne = 20; valueTwo = ++valueOne; Console.WriteLine("After prefix: {0}, {1}", valueOne, valueTwo); } } Output: After postfix: 11, 10 After prefix: 21, 21 3.6.4 Relational OperatorsRelational operators are used to compare two values, and then return a Boolean (true or false). The greater-than operator (>), for example, returns true if the value on the left of the operator is greater than the value on the right. Thus, 5 > 2 returns the value true, while 2 > 5 returns the value false. The relational operators for C# are shown in Table 3-3. This table assumes two variables: bigValue and smallValue, in which bigValue has been assigned the value 100 and smallValue the value 50.
Each of these relational operators acts as you might expect. However, take note of the equals operator (==), which is created by typing two equal signs (=) in a row (i.e., without any space between them); the C# compiler treats the pair as a single operator. The C# equality operator (==) tests for equality between the objects on either side of the operator. This operator evaluates to a Boolean value (true or false). Thus, the statement: myX == 5; evaluates to true if and only if myX is a variable whose value is 5.
3.6.5 Use of Logical Operators with ConditionalsIf statements (discussed earlier in this chapter) test whether a condition is true. Often you will want to test whether two conditions are both true, or whether only one is true, or none is true. C# provides a set of logical operators for this, as shown in Table 3-4. This table assumes two variables, x and y, in which x has the value 5 and y the value 7.
The and operator tests whether two statements are both true. The first line in Table 3-4 includes an example that illustrates the use of the and operator: (x == 3) && (y == 7) The entire expression evaluates false because one side (x == 3) is false. With the or operator, either or both sides must be true; the expression is false only if both sides are false. So, in the case of the example in Table 3-4: (x == 3) || (y == 7) the entire expression evaluates true because one side (y==7) is true. With a not operator, the statement is true if the expression is false, and vice versa. So, in the accompanying example: ! (x == 3) the entire expression is true because the tested expression (x==3) is false. (The logic is "it is true that it is not true that x is equal to 3.")
3.6.6 Operator PrecedenceThe compiler must know the order in which to evaluate a series of operators. For example, if I 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*3 is equal to 26 rather than 36. Both addition and multiplication 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#, parentheses are also used to change the order of precedence much as they are 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 3-5 summarizes operator precedence in C#.
In some complex equations you might need to nest your parentheses to ensure the proper order of operations. Let's assume I want to know how many seconds my family wastes each morning. It turns out that 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 my algorithm: (((minDrinkingCoffee + minReadingNewspaper )* numAdults ) + ((minDawdling + minArguing) * numChildren)) * secondsPerMinute. 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 important) debug. As you step through this program in your debugger, you can see the interim values and make sure they are correct. 3.6.7 The Ternary OperatorAlthough most operators require one term (e.g., myValue++) or two terms (e.g., a+b), there is one operator that has three: the ternary operator (?:). conditional-expression ? expression1 : expression2 This operator evaluates a conditional expression (an expression that returns a value of type bool), and then invokes either expression1 if the value returned from the conditional expression is true, or expression2 if the value returned is false. The logic is "if this is true, do the first; otherwise do the second." Example 3-18 illustrates. Example 3-18. The ternary operatorusing System; class Values { static void Main( ) { int valueOne = 10; int valueTwo = 20; int maxValue = valueOne > valueTwo ? valueOne : valueTwo; Console.WriteLine("ValueOne: {0}, valueTwo: {1}, maxValue: {2}", valueOne, valueTwo, maxValue); } } Output: ValueOne: 10, valueTwo: 20, maxValue: 20 In Example 3-18, the ternary operator is being used to test whether valueOne is greater than valueTwo. If so, the value of valueOne is assigned to the integer variable maxValue; otherwise the value of valueTwo is assigned to maxValue. |
[ Team LiB ] |