An 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.
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.
C# 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.
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 (%
),
described in the next section, to retrieve the remainder.
Note, however, that C# does return fractional answers when you divide floats, doubles, and decimals.
To 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 modulus
using 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: {0}
float: {1}
",
i1/i2, f1/f2);
Console.WriteLine("double: {0}
decimal: {1}",
d1/d2, dec1/dec2);
Console.WriteLine("
Modulus: {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: {0} float: {1} ", i1/i2, f1/f2);
It begins with a call to Console.Writeline
,
passing in this partial string:
"Integer: {0}
This will print the characters Integer:
followed
by a tab ( )
followed by the first parameter
({0}
) and then followed by a newline character
(
). The next string snippet:
float: {1}
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(" Modulus: {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.
A 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.
Suppose 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 you use the ++
operator, and to
decrement by 1 you use the --
operator.
Thus, if you want to increment the variable myAge
by 1 you can write:
myAge++;
To 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
), or you can 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. Illustrating prefix versus postfix increment
using 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
Relational
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
.
Table 3-3. C# relational operators (assumes bigValue = 100 and smallValue = 50)
Name |
Operator |
Given this statement: |
The expression evaluates to: |
---|---|---|---|
|
|
| |
|
|
| |
|
|
| |
Greater than or equals |
|
| |
|
|
| |
Less than or equals |
|
|
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
.
If
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 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 which 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.”)
The 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#.
Table 3-5. Operator precedence
In some complex equations you might need to nest your parentheses to ensure the proper order of operations. 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.
Although 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 (?:)
.
cond-expr?
expr1:
expr2
This operator evaluates a conditional expression
(an expression which 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 operator
using 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
.
18.224.68.28