C# statements are evaluated in order. The compiler starts at the
beginning of a statement list and makes its way to the bottom. This
would be entirely straightforward, and terribly limiting, were it not
for branching. There are two types of
branches in a C# program:
unconditional branching and
conditional branching.

Program flow is also affected by looping
and iteration statements, which are signaled by the keywords
for, while,
do, in, and
foreach. Iteration is discussed later in this
chapter. For now, let's consider some of the more
basic methods of conditional and unconditional branching.

3.5.1 Unconditional Branching Statements

An unconditional branch is created in one
of two ways. The first way is by invoking a method. When the compiler
encounters the name of a method, it stops execution in the current
method and branches to the newly
"called" method. When that method
returns a value, execution picks up in the original method on the
line just below the method call. Example 3-6
illustrates.

Program flow begins in Main( ) and proceeds until
SomeMethod( ) is invoked (invoking a method is
sometimes referred to as "calling"
the method). At that point, program flow branches to the method. When
the method completes, program flow resumes at the next line after the
call to that method.

The second way to create an unconditional branch is with one of the
unconditional branch keywords:
goto,
break,
continue,
return, or
throw. Additional information about the
first three jump statements is provided later in this chapter; the
final statement, throw, is discussed in Chapter 11.

3.5.2 Conditional Branching Statements

A conditional branch is created by a
conditional statement, which is signaled by keywords such as
if, else, or
switch. A conditional branch occurs only if the
condition expression evaluates true.

Cand C++ programmers take note: Unlike C and C++, in which any expression
can be used in a conditional, C# requires that all conditional
expressions evaluate to a Boolean value.

3.5.2.1 If...else statements

If...else statements branch based on a
condition. The condition is an expression, tested in the head of the
if statement. If the condition evaluates true, the
statement (or block of statements) in the body of the
if statement is executed.

If statements may contain an optional
else statement. The else
statement is executed only if the expression in the head of the
if statement evaluates false:

if (expression )
statement1
[else statement2 ]

This is the kind of description of the if
statement you are likely to find in your compiler documentation. It
shows you that the if statement takes a Boolean
expression (an expression that evaluates true or false) in
parentheses, and executes statement1 if the
expression evaluates true. Note that statement1
can actually be a block of statements within braces.

You can also see that the else statement is
optional, as it is enclosed in square brackets. Although this gives
you the syntax of an if statement, an illustration
will make its use clear. See Example 3-7.

In Example 3-7, the first if
statement tests whether valueOne is greater than
valueTwo. The relational operators such as greater
than (>), less than (<),
and equal to (==) are fairly intuitive to use.

The test of whether valueOne is greater than
valueTwo evaluates false (because
valueOne is 10 and valueTwo is
20, so valueOne is not
greater than valueTwo). The
else statement is invoked, printing the statement:

ValueTwo: 20 is larger than ValueOne: 10

The second if statement evaluates true and all the
statements in the if block are evaluated, causing
two lines to print:

Statement Blocks

You can substitute a statement block anywhere that C# expects
a statement. A statement block is a set of statements surrounded by
braces.

Thus, where you might write:

if (someCondition)
someStatement;

you can instead write:

if(someCondition)
{
statementOne;
statementTwo;
statementThree;
}

3.5.2.2 Nested if statements

It is possible, and not uncommon, to nest if statements
to handle complex conditions. For example, suppose you need to write
a program to evaluate the temperature, and specifically to return the
following types of information:

If the temperature is 32 degrees or lower, the program should warn
you about ice on the road.

If the temperature is exactly 32 degrees, the program should tell you
that there may be ice patches.

There are many good ways to write this program. Example 3-8 illustrates one approach, using nested
if statements.

The logic of Example 3-8 is that it tests whether
the temperature is less than or equal to 32. If so, it prints a
warning:

if (temp <= 32)
{
Console.WriteLine("Warning! Ice on road!");

The program then checks whether the temp is equal to 32 degrees. If
so, it prints one message; if not, the temp must be less than 32 and
the program prints the second message. Notice that this second
if statement is nested within the first
if, so the logic of the else is
"since it has been established that the temp is less
than or equal to 32, and it isn't equal to 32, it
must be less than 32."

All Operators Are Not Created Equal

A closer examination of the second if statement in
Example 3-8 reveals a common potential problem. This
if statement tests whether the temperature is
equal to 32:

if (temp == 32)

In C and C++, there is an inherent danger in this kind of statement.
It's not uncommon for novice programmers to use the
assignment operator rather than the
equals operator, instead creating the statement:

if (temp = 32)

This mistake would be difficult to notice, and the result would be
that 32 was assigned to temp,
and 32 would be returned as the value of the
assignment statement. Because any nonzero value evaluates to true in
C and C++, the if statement would return true. The
side effect would be that temp would be assigned a
value of 32 whether or not it originally had that
value. This is a common bug that could easily be
overlookedif the developers of C# had not
anticipated it!

C# solves this problem by requiring that if
statements accept only Boolean values. The 32
returned by the assignment is not Boolean (it is an integer) and, in
C#, there is no automatic conversion from 32 to
true. Thus, this bug would be caught at compile
time, which is a very good thing, and a significant improvement over
C++, at the small cost of not allowing implicit conversions from
integers to Booleans!

3.5.2.3 Switch statements: an alternative to nested ifs

Nested if statements are hard to read, hard to get
right, and hard to debug. When you have a complex set of choices to
make, the switch statement is a more powerful
alternative. The logic of a switch statement is "pick
a matching value and act accordingly."

As you can see, like an if statement, the
expression is put in parentheses in the head of the
switch statement. Each case statement then
requires a constant expression; that is, a literal or symbolic
constant or an enumeration.

If a case is matched, the statement (or block of statements)
associated with that case is executed. This must be followed by a
jump statement. Typically, the jump statement is
break, which transfers execution out of the
switch. An alternative is a goto statement,
typically used to jump into another case, as illustrated in Example 3-9.

In this whimsical example, we create constants for various political
parties. We then assign one value (Libertarian) to
the variable myChoice and switch on that value. If
myChoice is equal to Democrat,
we print out a statement. Notice that this case ends with
break. break is a jump
statement that takes us out of the switch
statement and down to the first line after the switch, on which we
print "Thank you for voting."

VB6 programmers take note: The equivalent
of the C# switch statement is the VB6
SelectCase statement. Also,
while VB6 allows you to test a range of values using a single Case
statement, C# syntax does not provide for this contingency. The
following two Case statements are syntactically
correct in VB6:

Case Is > 100
Case 50 to 60

However, these statements are not valid in C#.
In C#, you can test only a single constant expression. In order to
test a range, you must test each value independently and
"fall through" to a common
case block.

The value LiberalRepublican has no statement under
it, and it "falls through" to the
next statement: Republican. If the value is
LiberalRepublican or
Republican, the Republican
statements execute. You can only "fall
through" in this way if there is no body within the
statement. If you uncomment the WriteLine( ) under
LiberalRepublican, this program will not compile.

Cand C++ programmers take note: you cannot fall
through to the next case unless the case statement
is empty. Thus, you can write the following:

case 1: // fall through ok
case 2:

In this example, case1 is
empty. You cannot, however, write the following:

case 1:
TakeSomeAction( );
// fall through not OK
case 2:

Here case1 has a statement in
it, and you cannot fall through. If you want case1 to fall through to case2, you must explicitly use
goto:

If you do need a statement but you then want to execute another case,
you can use the goto statement, as shown in the
NewLeft case:

goto case Progressive;

It is not required that the goto take you to the
case immediately following. In the next instance, the
Libertarian choice also has a
goto, but this time it jumps all the way back up
to the Republican case. Because our value was set
to Libertarian, this is just what occurs. We print
out the Libertarian statement, go to the
Republican case, print that statement, and then
hit the break, taking us out of the switch and
down to the final statement. The output for all of this is:

Libertarians are voting Republican
You voted Republican.
Thank you for voting.

Note the default case, excerpted from Example 3-9:

default:
Console.WriteLine(
"You did not pick a valid choice.\n");

If none of the cases match, the default case will
be invoked, warning the user of the mistake.

3.5.2.4 Switch on string statements

In the previous example, the switch value was an integral constant.
C# offers the ability to switch on a string,
allowing you to write:

case "Libertarian":

If the strings match, the case statement is
entered.

3.5.3 Iteration Statements

C# provides an extensive suite of iteration statements, including
for, while and
do...while loops, as well as
foreach loops (new to the C family but familiar to
VB programmers). In addition, C# supports the
goto, break,
continue, and return jump
statements.

3.5.3.1 The goto statement

The goto statement is the seed from which all
other iteration statements have been germinated. Unfortunately, it is
a semolina seed, producer of spaghetti code and endless confusion.
Most experienced programmers properly shun the
goto statement, but in the interest of
completeness, here's how you use it:

Create a label.

goto that label.

The label is an identifier followed by a colon. The
goto command is typically tied to a condition, as
illustrated in Example 3-10.

Example 3-10. Using goto

If you were to try to draw the flow of control in a program that
makes extensive use of goto statements, the
resulting morass of intersecting and overlapping lines looks like a
plate of spaghetti; hence the term "spaghetti
code." It was this phenomenon that led to the
creation of alternatives, such as the while loop.
Many programmers feel that using goto in anything
other than a trivial example creates confusion and
difficult-to-maintain code.

3.5.3.2 The while loop

The semantics of the
while loop are "while this
condition is true, do this work." The syntax is:

while (expression) statement

As usual, an expression is any statement that returns a value.
While statements require an expression that
evaluates to a Boolean
(true/false) value, and that
statement can, of course, be a block of statements. Example 3-11 updates Example 3-10, using a
while loop.

Example 3-11. Using a while loop

The code in Example 3-11 produces results identical
to the code in Example 3-10, but the logic is a bit
clearer. The while statement is nicely
self-contained, and it reads like an English sentence:
"whilei is
less than 10, print this message and increment
i."

Notice that the while loop tests the value of
i before entering the loop. This ensures that the
loop will not run if the condition tested is false; thus if
i is initialized to 11, the loop will never run.

3.5.3.3 The do...while loop

There are times when a while loop might not serve
your purpose. In certain situations, you might want to reverse the
semantics from "run while this is
true" to the subtly different "do
this while this condition remains true." In other
words, take the action, and then, after the action is completed,
check the condition. For this you will use the
do...while loop.

do statement while expression

An expression is any statement that returns a value. Example 3-12 shows the do...while loop.

Example 3-12. The do...while loop

Here i is initialized to 11 and
the while test fails, but only after the body of
the loop has run once.

3.5.3.4 The for loop

A careful examination of the while loop in Example 3-11 reveals a pattern often seen in iterative
statements: initialize a variable (i=0), test the variable
(i<10),
execute a series of statements, and increment the variable
(i++). The
for loop allows you to combine all these steps
in a single loop statement:

This for loop makes use of the modulus operator
described later in this chapter. The value of i is
printed until i is a multiple of
10.

if (i%10 == 0)

A tab is then printed, followed by the value. Thus the 10s (20, 30,
40, etc.) are called out on the right side of the output.

VB6programmers take note: In C#, looping
variables are declared within the header of the
for or foreach statement
(rather than before the statement begins). This means that they are
in scope only within the block, and you cannot refer to them outside
the loop. The foreach statement is covered in
detail in Chapter 9.

The individual values are printed using Console.Write(
), which is much like WriteLine( ) but
which does not enter a newline character, allowing the subsequent
writes to occur on the same line.

A few quick points to notice: in a for loop, the
condition is tested before the statements are executed. Thus, in the
example, i is initialized to zero, then it is
tested to see if it is less than 100. Because i<100 returns
true, the statements within the
for loop are executed. After the execution,
i is incremented (i++).

Note that the variable i is scoped to within the
for loop (that is, the variable
i is visible only within the
for loop). Example 3-14 will not
compile.

Much of this is a matter of personal taste. Although I find
whitespace can make code more readable, too much space can cause
confusion. In this book, I tend to compress the whitespace to save
room on the printed page.

3.5.3.5 The foreach statement

The foreach statement is new to the C family of
languages; it is used for looping through the elements of an array or
a collection. Discussion of this incredibly useful statement is
deferred until Chapter 9.

3.5.3.6 The continue and break statements

There are times when you would like to restart a loop without
executing the remaining statements in the loop. The
continue statement causes the loop to return
to the top and continue executing.

The obverse side of that coin is the ability to break out of a loop
and immediately end all further work within the loop. For this
purpose the break statement exists.

Break and continue create
multiple exit points and make for hard-to-understand, and thus
hard-to-maintain, code. Use them with some care.

Example 3-15 illustrates the mechanics of
continue and break. This code,
suggested to me by one of my technical reviewers, Donald Xie, is
intended to create a traffic signal processing system. The signals
are simulated by entering numerals and uppercase characters from the
keyboard, using Console.ReadLine( ), which reads a
line of text from the keyboard.

The algorithm is simple: receipt of a 0 (zero) means normal
conditions, and no further action is required except to log the
event. (In this case, the program simply writes a message to the
console; a real application might enter a timestamped record in a
database.) On receipt of an abort signal (here simulated with an
uppercase "A"), the problem is
logged and the process is ended. Finally, for any other event, an
alarm is raised, perhaps notifying the police. (Note that this sample
does not actually notify the police, though it does print out a
harrowing message to the console.) If the signal is
"X," the alarm is raised, but the
while loop is also terminated.

The point of this exercise is that when the A
signal is received, the action in the if statement
is taken and then the program breaks out of the
loop, without raising the alarm. When the signal is
0, it is also undesirable to raise the alarm, so
the program continues from the top of the
loop.