There are many situations in which you will want to do the same thing again and again, perhaps slightly changing a value each time you repeat the action. This is called iteration, or looping . Typically, you’ll iterate (or loop) over a set of items, taking the same action on each item in the collection. This is the programming equivalent of an assembly line. On an assembly line, you might take a hundred car bodies and put a windshield on each one as it comes by. In an iterative program, you might work your way through a collection of text boxes on a form, retrieving the value from each in turn and using those values to update a database.
C# provides an extensive suite of iteration statements, including
for
and while
, and also do...while
and foreach
loops. You can also create a loop by
using the goto
statement. The
remainder of this chapter considers the use of goto
, for
,
while
, and do...while
. However, we’ll postpone coverage
of foreach
until Chapter 10.
The goto
statement
was used earlier in this chapter as an unconditional branch in a
switch
statement. The more common
use of goto
, however, is to create
a loop. In fact, the goto
statement
is the seed from which all other looping statements have been germinated. Unfortunately, it is a semolina
seed, producer of "spaghetti code" (see
the following sidebar) and endless confusion.
Because of the problems created by the goto
statement, it is rarely used in C#
outside of switch
statements, but
in the interest of completeness, here’s how you create goto
loops:
Create a label.
goto
that label.
The label is an identifier followed by a
colon. You place the label in your code, and then you use the goto
keyword to jump to that label. The
goto
command is typically tied to
an if
statement, as illustrated in
Example 5-11.
Example 5-11. Using goto
using System; public class Tester { public static void Main( ) { int counterVariable = 0; repeat: // the label Console.WriteLine( "counterVariable: {0}", counterVariable ); // increment the counter counterVariable++; if ( counterVariable < 10 ) goto repeat; // the dastardly deed } }
The output looks like this:
counterVariable: 0 counterVariable: 1 counterVariable: 2 counterVariable: 3 counterVariable: 4 counterVariable: 5 counterVariable: 6 counterVariable: 7 counterVariable: 8 counterVariable: 9
This code is not terribly complex; you’ve used only a single
goto
statement. However, with
multiple such statements and labels scattered through your code,
tracing the flow of execution becomes very difficult.
It was the phenomenon of spaghetti code that led to the creation
of alternatives, such as the while
loop.
The semantics of the while
loop are “while this condition is
true, do this work.” The syntax is:
while (Boolean expression
)statement
As usual, a Boolean expression is any statement that evaluates
to true or false. The statement executed within a while
statement can of course be a block of
statements within braces. Example 5-12 illustrates the
use of the while
loop.
Example 5-12. The while loop
using System; public class Tester { public static void Main( ) { int counterVariable = 0; // while the counter variable is less than 10 // print out its value while ( counterVariable < 10 ) { Console.WriteLine( "counterVariable: {0}", counterVariable ); counterVariable++; } } }
The output looks like this:
counterVariable: 0 counterVariable: 1 counterVariable: 2 counterVariable: 3 counterVariable: 4 counterVariable: 5 counterVariable: 6 counterVariable: 7 counterVariable: 8 counterVariable: 9
The code in Example
5-12 produces results identical to the code in Example 5-11, but the logic
is a bit more clear. The while
statement is nicely self-contained, and it reads like an English
sentence: “while counterVariable
is
less than 10, print this message and increment counterVariable
.”
Notice that the while
loop
tests the value of counterVariable
before entering the loop. This ensures that the loop will not run if
the condition tested is false. Thus, if counterVariable
is initialized to 11, the
loop will never run.
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. Such a loop will
always run at least once.
To ensure that the action is taken before the condition is
tested, use a do...while
loop:
dostatement
while (boolean-expression
);
The syntax is to write the keyword do
, followed by your statement (or block),
the while
keyword, and the
condition to test in parentheses. End the statement with a
semicolon.
Example 5-13
rewrites Example 5-12
to use a do...while
loop.
Example 5-13. The do...while loop
using System; public class Tester { public static void Main( ) { int counterVariable = 11; // display the message and then test that the value is // less than 10 do { Console.WriteLine( "counterVariable: {0}", counterVariable ); counterVariable++; } while ( counterVariable < 10 ); } }
The output looks like this:
counterVariable: 11
In Example 5-13,
counterVariable
is initialized to
11 and the while
test fails, but
only after the body of the loop has run once.
A careful examination of the while
loop in Example 5-12 reveals a
pattern often seen in iterative statements: initialize a variable
(counterVariable=0
), test the
variable (counterVariable<10
),
execute a series of statements, and increment the variable (counterVariable++
). The for
loop allows you to combine all these
steps in a single statement. You write a for
loop with the keyword for
, followed by the for
header, using the syntax:
for ([initializers
]; [expression
]; [iterators
])statement
The first part of the header is the initializer, in which you initialize a variable. The second part is the Boolean expression to test. The third part is the iterator, in which you update the value of the counter variable. All of this is enclosed in parentheses.
A simple for
loop is shown in
Example 5-14.
Example 5-14. A for loop
using System; public class Tester { public static void Main( ) { for ( int counter = 0; counter < 10; counter++ ) { Console.WriteLine( "counter: {0} ", counter ); } } }
The output looks like this:
counter: 0 counter: 1 counter: 2 counter: 3 counter: 4 counter: 5 counter: 6 counter: 7 counter: 8 counter: 9
The counter
variable is
initialized to zero in the initializer:
for (int counter=0
; counter<10; counter++)
The value of counter
is
tested in the expression part of the header:
for (int counter=0;counter<10
; counter++)
Finally, the value of counter
is incremented in the iterator part of the header:
for (int counter=0; counter<10;counter++
)
The initialization part runs only once, when the for
loop begins. The integer value counter
is created and initialized to zero, and the test is then executed.
Because counter
is less than 10,
the body of the for
loop runs and
the value is displayed.
After the loop completes, the iterator part of the header runs
and counter
is incremented. The
value of the counter
is tested,
and, if the test evaluates true, the body of the for
statement is executed again.
Your iterator doesn’t just have to be ++
. You can use—, or any other expression
that changes the value of the counter variable, as the needs of your
program dictate. Also, for the purposes of a for
loop, counter++
and ++counter
will have the same
result.
The logic of the for
loop is
as if you said, “For every value of counter that I initialize to zero,
take this action if the test returns true, and after the action,
update the value of counter.”
The modulus operator really comes into its own in
controlling for
loops. 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 whether %n
is equal to zero, as illustrated in
Example 5-15.
Example 5-15. Using modulus to find the tenth iteration
using System; public class Tester { public static void Main( ) { for ( int counter = 1; counter <= 100; counter++ ) { Console.Write( "{0} ", counter ); if ( counter % 10 == 0 ) { Console.WriteLine( " {0}", counter ); } // end if } // end for } // end Main } // end namespace
The output looks like this:
1 2 3 4 5 6 7 8 9 10 10 11 12 13 14 15 16 17 18 19 20 20 21 22 23 24 25 26 27 28 29 30 30 31 32 33 34 35 36 37 38 39 40 40 41 42 43 44 45 46 47 48 49 50 50 51 52 53 54 55 56 57 58 59 60 60 61 62 63 64 65 66 67 68 69 70 70 71 72 73 74 75 76 77 78 79 80 80 81 82 83 84 85 86 87 88 89 90 90 91 92 93 94 95 96 97 98 99 100 100
In Example
5-15, the value of the counter variable is incremented each
time through the loop. Within the loop, the value of counter is
compared with the result of modulus 10 (counter % 10
). When this evaluates to
zero, the value of counter is evenly divisible by 10, and the value
is printed in the righthand column.
It is possible to exit from a for
loop even before the test condition
has been fulfilled. To end a for
loop prematurely, use the unconditional branching statement break
.
The break
statement halts
the for
loop, and execution
resumes after the for
loop
statement (or closing brace), as in Example 5-16.
Example 5-16. Using break to exit a for loop
using System; public class Tester { public static void Main( ) { for ( int counter = 0; counter < 10; counter++ ) { Console.WriteLine( "counter: {0} ", counter ); // if condition is met, break out. if ( counter == 5 ) { { Console.WriteLine( "Breaking out of the loop" ); break; } } Console.WriteLine( "For loop ended" ); } } }
The output looks like this:
counter: 0 counter: 1 counter: 2 counter: 3 counter: 4 counter: 5 Breaking out of the loop For loop ended
In this for
loop, you test
whether the value counter is equal to 5. If that value is found (and
in this case, it always will be), you break out of the loop.
Rather than breaking out of a loop, you may at times
want the semantics of saying, “Don’t execute any more statements in
this loop, but start the loop again from the top of the next
iteration.” To accomplish this, use the unconditional branching
statement continue
.
Break
and continue
create multiple exit points and
make for hard-to-understand, and thus hard-to-maintain, code. Use
them with care.
Example 5-17
illustrates the mechanics of both 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.
Example 5-17. Break and continue
using System; public class Tester { public static int Main( ) { string signal = "0"; // initialize to neutral while ( signal != "X" ) // X indicates stop { Console.Write( "Enter a signal. X = stop. A = Abort: " ); signal = Console.ReadLine( ); // do some work here, no matter what signal you // receive Console.WriteLine( "Received: {0}", signal ); if ( signal == "A" ) { // faulty - abort signal processing // Log the problem and abort. Console.WriteLine( "Fault! Abort " ); break; } if ( signal == "0" ) { // normal traffic condition // log and continue on Console.WriteLine( "All is well. " ); continue; } // Problem. Take action and then log the problem // and then continue on Console.WriteLine( "{0} -- raise alarm! ", signal ); } return 0; } }
The signals are simulated by entering numerals and uppercase
characters from the keyboard, using the Console.ReadLine( )
method, which reads a
line of text from the keyboard. ReadLine( )
reads a line of text into a string variable. The string
ends when you press A.
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 time-stamped record in a database.)
On receipt of an Abort signal (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.
Here’s one sample output:
Enter a signal. X = stop. A = Abort: 0 Received: 0 All is well. Enter a signal. X = stop. A = Abort: 1 Received: 1 1 -- raise alarm! Enter a signal. X = stop. A = Abort: X Received: X X -- raise alarm!
Here’s a second sample output:
Enter a signal. X = stop. A = Abort: A Received: A Fault! Abort
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.
You will remember that the for
loop header has three
parts—initialization, expression, and iteration—and the syntax is as
follows:
for ([initializers
]; [expression
]; [iterators
])statement
Each part of the for
loop
header is optional. You can, for example, initialize the value
outside the for
loop, as shown in
Example 5-18.
Example 5-18. No initialization with for loop
using System; public class Tester { public static void Main( ) { int counter = 0; // some work here counter = 3; // more work here for ( ; counter < 10; counter++ ) { Console.WriteLine( "counter: {0} ", counter ); } } }
The output looks like this:
counter: 3 counter: 4 counter: 5 counter: 6 counter: 7 counter: 8 counter: 9
In this example, the counter variable was initialized and
modified before the for
loop
began. Notice that a semicolon is used to hold the place of the
missing initialization statement.
You can also leave out the iteration step if you have reason to increment the counter variable inside the loop, as shown in Example 5-19.
Example 5-19. Leaving out the iterator step
using System; public class Tester { public static void Main( ) { for ( int counter = 0; counter < 10; ) // no increment { Console.WriteLine( "counter: {0} ", counter ); // do more work here counter++; // increment counter } } }
You can mix and match which statements you leave out of a
for
loop.
If you create a for
loop
with no initializer or incrementer, like this:
for ( ; counter < 10 ; )
you have a while
loop in
for
loop’s clothing; and of
course that construct is silly, and thus not used very
often.
It is even possible to leave all the statements out, creating what is known as a forever loop:
for ( ;; )
You break out of a forever (or while(true)
) loop with a break
statement. A forever loop is shown
in Example
5-20.
Example 5-20. A forever loop
using System; public class Tester { public static void Main( ) { int counterVariable = 0; // initialization for ( ; ; ) // forever { Console.WriteLine( "counter: {0} ", counterVariable++ ); // increment if ( counterVariable > 10 ) // test break; } } }
The output looks like this:
counter: 0 counter: 1 counter: 2 counter: 3 counter: 4 counter: 5 counter: 6 counter: 7 counter: 8 counter: 9 counter: 10
Use a forever loop to indicate that the “normal” case is to continue the loop indefinitely; for example, if your program is waiting for an event to occur somewhere in the system. The conditions for breaking out of the loop would then be exceptional and managed inside the body of the loop.
Although it is possible to use a forever loop to good effect,
Example 5-20 is a
degenerate case. The initialization, increment, and test would be
done more cleanly in the header of the for
loop, and the program would then be
easier to understand. It is shown here to illustrate that a forever
loop is possible.
You can accomplish exactly the same semantics of a
forever loop using the while (true)
construct, as shown in Example 5-21.
Example 5-21. The while (true) construct
using System; public class Tester { public static void Main( ) { int counterVariable = 0; // initialization while ( true ) { Console.WriteLine( "counter: {0} ", counterVariable++ ); // increment if ( counterVariable > 10 ) // test break; } } }
The output looks like this:
counter: 0 counter: 1 counter: 2 counter: 3 counter: 4 counter: 5 counter: 6 counter: 7 counter: 8 counter: 9 counter: 10
Example 5-21 is identical to Example 5-20, except that the forever construct:
for ( ;; )
is replaced with a:
while (true)
statement. Of course, the keyword true
always returns the Boolean value
true; so like the forever loop, this while
loop runs until the break
statement is executed.