Control Flow Statements, Continued
Now that we’ve described Boolean expressions in more detail we can more clearly describe the control flow statements supported by C#. Many of these statements will be familiar to experienced programmers, so you can skim this section looking for details specific to C#. Note in particular the foreach loop, as this may be new to many programmers.
The while and do/while Loops
Thus far you have learned how to write programs that do something only once. However, computers can easily perform similar operations multiple times. In order to do this, you need to create an instruction loop. The first instruction loop we will discuss is the while loop, because it is the simplest conditional loop. The general form of the while statement is as follows:
while (condition) statement
The computer will repeatedly execute the statement that is the “body” of the loop as long as the condition (which must be a Boolean expression) evaluates to true. If the condition evaluates to false, code execution skips the body and executes the code following the loop statement. Note that statement will continue to execute even if it causes the condition to become false. It isn’t until the condition is reevaluated “at the top of the loop” that the loop exits. The Fibonacci calculator shown in Listing 3.41 demonstrates the while loop.
Listing 3.41. while Loop Example
class
FibonacciCalculator {static void
Main() {decimal
current;decimal
previous;decimal
temp;decimal
input; System.Console.Write("Enter a positive integer:"
); // decimal.Parse convert the ReadLine to a decimal. input =decimal
.Parse(System.Console.ReadLine()); // Initialize current and previous to 1, the first // two numbers in the Fibonacci series. current = previous = 1; // While the current Fibonacci number in the series is // less than the value input by the user.while(current <= input)
{
temp = current;
current = previous + current;
previous = temp; // Executes even if previous
// statement caused current to exceed input
}
System.Console.WriteLine("The Fibonacci number following this is {0}"
, current); } }
A Fibonacci number is a member of the Fibonacci series, which includes all numbers that are the sum of the previous two numbers in the series, beginning with 1 and 1. In Listing 3.41, you prompt the user for an integer. Then you use a while loop to find the first Fibonacci number that is greater than the number the user entered.
The do/while loop is very similar to the while loop except that a do/while loop is preferred when the number of repetitions is from 1 to n and n is not known when iterating begins. This pattern frequently occurs when prompting a user for input. Listing 3.42 is taken from the tic-tac-toe program.
Listing 3.42. do/while Loop Example
// Repeatedly request player to move until he // enter a valid position on the board. bool valid;do
{ valid =false
; // Request a move from the current player. System.Console.Write("\nPlayer {0}: Enter move:"
, currentPlayer); input = System.Console.ReadLine(); // Check the current player's input. // ... }while
(!valid);
In Listing 3.42, you always initialize valid to false at the beginning of each iteration, or loop repetition. Next, you prompt and retrieve the number the user input. Although not shown here, you then check whether the input was correct, and if it was, you assign valid equal to true. Since the code uses a do/while statement rather than a while statement, the user will be prompted for input at least once.
The general form of the do/while loop is as follows:
do statement while (condition);
As with all the control flow statements, a code block is generally used as the single statement in order to allow multiple statements to be executed as the loop body. However, any single statement except for a labeled statement or a local variable declaration can be used.
The for Loop
The for loop iterates a code block until a specified condition is reached. In that way, it is very similar to the while loop. The difference is that the for loop has built-in syntax for initializing, incrementing, and testing the value of a counter, known as the loop variable. Because there is a specific location in the loop syntax for an increment operation, the increment and decrement operators are frequently used as part of a for loop.
Listing 3.43 shows the for loop used to display an integer in binary form. The results of this listing appear in Output 3.21.
Listing 3.43. Using the for Loop
public class
BinaryConverter {public static void
Main() {const int
size = 64;ulong
value;char
bit; System.Console.Write ("Enter an integer: "
); // Use long.Parse() so as to support negative numbers // Assumes unchecked assignment to ulong. value = (ulong
)long
.Parse(System.Console.ReadLine()); // Set initial mask to 100....ulong
mask = 1UL << size - 1;for
(int
count = 0; count < size; count++) { bit = ((mask & value) > 0) ?'1'
:'0'
; System.Console.Write(bit); // Shift mask one location over to the right mask >>= 1; } } }
Output 3.21.
Enter an integer: -42 1111111111111111111111111111111111111111111111111111111111010110
Listing 3.43 performs a bit mask 64 times, once for each bit in the number. The three parts of the for loop header first declare and initialize the variable count, then describe the condition that must be met for the loop body to be executed, and finally describe the operation that updates the loop variable. The general form of the for loop is as follows:
for (initial ; condition ; loop) statement
Here is a breakdown of the for loop.
- The initial section performs operations that precede the first iteration. In Listing 3.43, it declares and initializes the variable count. The initial expression does not have to be a declaration of a new variable (though it frequently is). It is possible, for example, to declare the variable beforehand and simply initialize it in the for loop, or to skip the initialization section entirely by leaving it blank. Variables declared here are in scope throughout the header and body of the for statement.
- The condition portion of the for loop specifies an end condition. The loop exits when this condition is false exactly like the while loop does. The for loop will execute the body only as long as the condition evaluates to true. In the preceding example, the loop exits when count is greater than or equal to 64.
- The loop expression executes after each iteration. In the preceding example, count++ executes after the right shift of the mask (mask >>= 1), but before the condition is evaluated. During the sixty-fourth iteration, count is incremented to 64, causing the condition to become false, and therefore terminating the loop.
- The statement portion of the for loop is the “loop body” code that executes while the conditional expression remains true.
If you wrote out each for loop execution step in pseudocode without using a for loop expression, it would look like this.
- Declare and initialize count to 0.
- If count is less than 64, continue to step 3; otherwise, go to step 7.
- Calculate bit and display it.
- Shift the mask.
- Increment count by one.
- Jump back to line 2.
- Continue the execution of the program after the loop.
The for statement doesn’t require any of the elements in its header. for(;;){ ... } is perfectly valid; although there still needs to be a means to escape from the loop to avoid executing infinitely. (If the condition is missing, it is assumed to be the constant true.)
The initial and loop expressions have an unusual syntax to support loops that require multiple loop variables, as shown in Listing 3.44.
Listing 3.44. for Loop Using Multiple Expressions
for
(int
x=0, y=5; ((x<=5) && (y>=0));y--, x++
) { System.Console.Write("{0}{1}{2}\t"
, x, (x>y?'>'
:'<'
), y); }
The results of Listing 3.44 appear in Output 3.22.
Output 3.22.
0<5 1<4 2<3 3>2 4>1 5>0
Here the initialization clause contains a complex declaration that declares and initializes two loop variables, but this is at least similar to a declaration statement that declares multiple local variables. The loop clause is quite unusual, as it can consist of a comma-separated list of expressions, not just a single expression.
The for loop is little more than a more convenient way to write a while loop; you can always rewrite a for loop like this:
{ initial; while(condition) { statement; loop; } }
The foreach Loop
The last loop statement in the C# language is foreach. The foreach loop iterates through a collection of items, setting a loop variable to represent each item in turn. In the body of the loop, operations may be performed on the item. A nice property of the foreach loop is that every item is iterated over exactly once; it is not possible to accidentally miscount and iterate past the end of the collection as can happen with other loops.
The general form of the foreach statement is as follows:
foreach(type variable in collection) statement
Here is a breakdown of the foreach statement.
- type is used to declare the data type of the variable for each item within the collection. It may be var, in which case the compiler infers the type of the item from the type of the collection.
- variable is a read-only variable into which the foreach loop will automatically assign the next item within the collection. The scope of the variable is limited to the body of the loop.
- collection is an expression, such as an array, representing any number of items.
- statement is the loop body that executes for each iteration of the loop.
Consider the foreach loop in the context of the simple example shown in Listing 3.45.
Listing 3.45. Determining Remaining Moves Using the foreach Loop
class
TicTacToe // Declares the TicTacToe class. {static void
Main() // Declares the entry point of the program. { // Hardcode initial board as follows // ---+---+--- // 1 | 2 | 3 // ---+---+--- // 4 | 5 | 6 // ---+---+--- // 7 | 8 | 9 // ---+---+---char
[] cells = {'1'
,'2'
,'3'
,'4'
,'5'
,'6'
,'7'
,'8'
,'9'
}; System.Console.Write("The available moves are as follows: "
); // Write out the initial available movesforeach (char cell in cells)
{
if (cell != 'O' && cell != 'X')
{
System.Console.Write("{0} ", cell);
}
}
} }
Output 3.23 shows the results of Listing 3.45.
Output 3.23.
The available moves are as follows: 1 2 3 4 5 6 7 8 9
When the execution engine reaches the foreach statement, it assigns to the variable cell the first item in the cells array—in this case, the value '1'. It then executes the code within the block that makes up the foreach loop body. The if statement determines whether the value of cell is 'O' or 'X'. If it is neither, the value of cell is written out to the console. The next iteration then assigns the next array value to cell, and so on.
It is important to note that the compiler prevents modification of the variable (cell) during the execution of a foreach loop. Also, the loop variable has a subtly different behavior in C# 5 than it did in previous versions; the difference is only apparent when the loop body contains a lambda expression or anonymous method that uses the loop variable. See Chapter 12 for details.
The switch Statement
A switch statement is simpler to understand than a complex if statement when you have a value that must be compared against may different constant values. The switch statement looks like this:
switch(expression) { case constant: statements default: statements }
Here is a breakdown of the switch statement.
- expression is the value that is being compared against the different constants. The type of this expression determines the “governing type” of the switch. Allowable governing data types are bool, sbyte, byte, short, ushort, int, uint, long, ulong, char, any enum type (covered in Chapter 8), the corresponding nullable types of each of those value types, and string.
- constant is any constant expression compatible with the governing type.
- A group of one or more case labels (or the default label) followed by a group of one or more statements is called a switch section. The pattern above has two switch sections; Listing 3.47 shows a switch statement with three switch sections.
- statements is one or more statements to be executed when the expression equals one of the constant values mentioned in a label in the switch section. The end point of the group of statements must not be reachable. Typically the last statement is a jump statement such as a break, return, or goto statement.
A switch statement should have at least one switch section; switch(x){} is legal but will generate a warning. Also, earlier the guideline was to avoid omitting braces in general. One exception is to omit braces for case and break statements because they serve to indicate the beginning and end of a block.
Listing 3.47, with a switch statement, is semantically equivalent to the series of if statements in Listing 3.46.
Listing 3.47. Replacing the if Statement with a switch Statement
static bool
ValidateAndMove(int
[] playerPositions,int
currentPlayer,string
input) {bool
valid =false
; // Check the current player's input.switch
(input) {case "1"
:case "2"
:case "3"
:case "4"
:case "5"
:case "6"
:case "7"
:case "8"
:case "9"
: // Save/move as the player directed. ... valid =true
;break
;case
"" :case "quit"
: valid =true
;break
;default
: // If none of the other case statements // is encountered then the text is invalid. System.Console.WriteLine("\nERROR: Enter a value from 1-9. "
+"Push ENTER to quit"
);break
; }return
valid; }
In Listing 3.47, input is the test expression. Since input is a string, the governing type is string . If the value of input is one of the strings 1, 2, 3, 4, 5, 6, 7, 8, or 9, the move is valid and you change the appropriate cell to match that of the current user’s token (X or O). Once execution encounters a break statement, control leaves the switch statement.
The next switch section describes how to handle the empty string or the string quit; it sets valid to true if input equals either value. The default switch section is executed if no other switch section had a case label that matched the test expression.
There are several things to note about the switch statement.
- A switch statement with no switch sections will generate a compiler warning, but the statement will still compile.
- Switch sections can appear in any order; the default section does not have to appear last. In fact, the default switch section does not have to appear at all; it is optional.
- The C# language requires that the end point of every switch section, including the last section, be unreachable. This means that switch sections usually end with a break, return, or goto.