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 it may be new to many programmers.
Thus far you have learned how to write programs that do something only once. However, computers can easily perform similar operations multiple times. To do so, you need to create an instruction loop. The first instruction loop we 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 repeatedly executes 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 continues to execute even if it causes the condition to become false. The loop exits only when the condition is reevaluated “at the top of the loop.” The Fibonacci calculator shown in Listing 4.44 demonstrates the while loop.
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 4.44, 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 remainder of this chapter considers other statements that cause a block of code to execute repeatedly. The term loop body refers to the statement (frequently a code block) that is to be executed within the while statement, since the code is executed in a “loop” until the exit condition is achieved. It is important to understand which loop construct to select. You use a while construct to iterate while the condition evaluates to true. You use a for loop whenever the number of repetitions is known, such as when counting from 0 to n. A do/while loop is similar to a while loop, except that it always executes the loop body at least once.
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 4.45 is taken from the tic-tac-toe program.
In Listing 4.45, you 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 is 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 for the statement in the general form. This of course allows multiple statements to be executed as the loop body. However, C# also allows any single statement except for a labeled statement (see the switch statement later in the chapter) or a local variable declaration.
The for loop iterates a code block until reaching a specified condition. In that way, it is like the while loop. The difference is that the for loop has built-in syntax for initializing, incrementing, and evaluating 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 4.46 shows the for loop used to display an integer in binary form. The results of this listing appear in Output 4.21.
Listing 4.46 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:
If you wrote out each for loop execution step in pseudocode without using a for loop expression, it would look like this:
The for statement doesn’t require any of the elements in its header. The expression for(;;){ ... } is perfectly valid, although there still needs to be a means to escape from the loop so that it will not continue to execute indefinitely. (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 4.47 (with Output 4.22).
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 because 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 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:
Consider the foreach loop in the context of the simple example shown in Listing 4.48 with Output 4.23.
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.
Note that the compiler prevents modification of the variable (cell) during the execution of a foreach loop.
Sometimes you might compare the same value in several continuous if statements, as shown with the input variable in Listing 4.49.
This code validates the text entered to ensure that it is a valid tic-tac-toe move. If the value of input were 9, for example, the program would have to perform nine different evaluations. It would be preferable to jump to the correct code after only one evaluation. To enable this, you use a switch statement.
A basic switch statement is simpler to understand than a complex if statement when you have a value that must be compared against different constant values. The switch statement looks like this:
switch (expression)
{
case constant:
statements
default:
statements
}
Here is a breakdown of the switch statement:
A switch statement should have at least one switch section; switch(x){} is legal but will generate a warning. Also, the guideline provided earlier was to avoid omitting braces in general. One exception to this rule of thumb is to omit braces for case and break statements because they serve to indicate the beginning and end of a block.
Listing 4.50, with a switch statement, is semantically equivalent to the series of if statements in Listing 4.49.
In Listing 4.50, 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.
In C++, if a switch section does not end with a jump statement, control “falls through” to the next switch section, executing its code. Because unintended fall-through is a common error in C++, C# does not allow control to accidentally fall through from one switch section to the next. The C# designers believed it was better to prevent this common source of bugs and encourage better code readability than to match the potentially confusing C++ behavior. If you do want one switch section to execute the statements of another switch section, you may do so explicitly with a goto statement, as demonstrated later in this chapter.
There are several things to note about the switch statement:
C# 7.0 introduced an improvement to the switch statement that enables pattern matching so that any data type—not just the limited few identified earlier—can be used for the switch expression. Pattern matching enables the use of switch statements based on the type of the switch expression and the use of case labels that also declare variables. Lastly, pattern matching switch statements support conditional expressions so that not only the type but also a Boolean expression at the end of the case label can identify which case label should execute. For more information on pattern matching with switch statements and expressions, see Chapter 7.