Team LiB   Previous Section   Next Section

7.4 Relational Operators

Relational operators compare two values and then return a Boolean value (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 7-1. This table assumes two variables: bigValue and smallValue, in which bigValue has been assigned the value 100 and smallValue the value 50.

Table 7-1. C# relational operators (assumes bigValue = 100 and smallValue = 50)

Name

Operator

Given this statement

The expression evaluates to

Equals

==

bigValue == 100

bigValue == 80

True

False

Not Equals

!=

bigValue != 100

bigValue != 80

False

True

Greater than

>

bigValue > smallValue

True

Greater than or equal to

>=

bigValue >= smallValue

smallValue >= bigValue

True

False

Less than

<

bigValue < smallValue

False

Less than or equal to

<=

smallValue <= bigValue

bigValue <= smallValue

True

False

Each of these relational operators acts as you might expect. Notice that most of these operators are composed of two characters. For example, the greater than or equal to operator (>=) is created with the greater than symbol (>) and the equal sign (=). Notice also that the equals operator is created with two equal signs (==) because the single equal sign alone (=) is reserved for the assignment operator.

It is not uncommon to confuse the assignment operator (=) with the equals operator (==). Just remember that the latter has two equal signs, the former only one.

The C# equals 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 the myX variable has a value of 5.

7.4.1 Use of Logical Operators with Conditionals

If statements (discussed in Chapter 6) test whether a condition is true. Often you will want to test whether two conditions are both true, only one is true, or neither is true. C# provides a set of logical operators for this, shown in Table 7-2. The examples in this table assume two variables, x and y, in which x has the value 5 and y the value 7.

Table 7-2. Logical operators

Name

Operator

Given this statement

The expression evaluates to

Logic

And

&&
(x == 3) && (y == 7)

False

Both must be true

Or

||
(x == 3) || (y == 7)

True

Either or both must be true

Not

!
! (x == 3)

True

Expression must be false

The and operator tests whether two statements are both true. The first line in Table 7-2 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. (Remember that x=5 and y=7.)

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 7-2:

(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.")

7.4.2 The Conditional Operator

Although most operators are unary (require one term, e.g., myValue++) or binary (two terms, e.g., a+b), there is one ternary (three terms) operator: the conditional operator (?:).

cond-expr  ?  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 7-6 illustrates.

Example 7-6. 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 7-6, 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.

7.4.3 Short-Circuit Evaluation

Consider the following code snippet:

int x = 8;
if ((x == 8) || (y == 12))

The if statement here is a bit complicated. The entire if statement is in parentheses, as are all if statements in C#. Thus, everything within the outer set of parentheses must evaluate true for the if statement to be true.

Within the outer parentheses are two expressions, (x == 8) and (y == 12), which are separated by an or operator (||). Because x is 8, the first term (x == 8) evaluates true. There is no need to evaluate the second term (y == 12). It doesn't matter whether y is 12; the entire expression will be true. Similarly, consider this snippet:

int x = 8;
if ((x == 5) && (y == 12))

Again, there is no need to evaluate the second term. Because the first term is false, the and must fail. (Remember, for an and statement to evaluate true, both tested expressions must evaluate true.)

In cases such as these, the C# compiler will short-circuit the evaluation; the second test will never be performed.

7.4.4 Operator Precedence

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 7-3 summarizes operator precedence in C#, using x and y as possible terms to be operated upon.[1]

[1] See my more advanced book Programming C#, Second Edition (O'Reilly) for more information on the more obscure operators.

Table 7-3. Precedence

Category

Operators

Primary

(x)  x.y x->y  f(x)  a[x] x++  x--  new  typeof sizeof checked  
unchecked stackalloc

Unary

+ - ! ~ ++x --x (T)x *x &x

Multiplicative

* / %

Additive

+ -

Shift

<< >>

Relational

< > <= >= is as

Equality

== !=

Logical AND

&

Logical XOR

^

Logical OR

|

Conditional AND

&&

Conditional OR

||

Conditional

?:

Assignment

= *= /= %= += -= <<= >>= &= ^= |=

The operators are listed in precedence order according to the category in which they fit. That is, the primary operators (e.g., ++) are evaluated before the unary operators (e.g., !). Multiplication is evaluated before addition.

In some complex equations, you might need to nest parentheses to ensure the proper order of operations. For example, assume I want to know how many seconds my family wastes each morning. 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 importantly) debug. As you step through this program in your debugger, you can see the interim values and make sure they are correct. See Chapter 10 for more information.

    Team LiB   Previous Section   Next Section