One of the computer's greatest strengths is its ability to perform the exact same calculation again and again without getting bored or making careless mistakes. It can calculate the average test scores for a dozen students, print a hundred advertisements, or compute the monthly bills for a million customers with no trouble or complaining.
The lessons you've read so far, however, don't tell you how to do these things. So far every step the computer takes requires a separate line of code. To calculate bills for a million customers, you would need to write at least a million lines of code!
In this lesson you learn how to make the computer execute the same lines of code many times. You learn how to loop through arrays and collections of items to take action or perform calculations on them.
The following sections describe the kinds of loops provided by C#. The final section describes two statements you can use to change the way a loop works: break
and continue
.
A for
loop uses a variable to control the number of times it executes a series of statements. The for
loop's syntax is as follows:
for (initialization; doneTest; next)
{
statements…
}
Where:
initialization
gets the loop ready to start. Usually this part declares and initializes the looping variable.doneTest
is a boolean expression that determines when the loop stops. The loop continues running as long as this expression is true.next
prepares the loop for its next iteration. Usually this increments the looping variable declared in the initialization
.statements
are the statements that you want the loop to execute.Note that none of the initialization
, doneTest
, or next
statements are required, although they are all used by the simplest kinds of for
loops.
For example, the following code displays the numbers 0 through 9 followed by their squares in the Console window:
for (int i = 0; i < 10; i++)
{
int iSquared = i * i;
Console.WriteLine(string.Format("{0}: {1}", i, iSquared));
}
In this code the initialization
statement declares the variable i
and sets it to 0, the next
statement adds 1 to i
, and the doneTest
keeps the loop running as long as i
< 10.
Here's a slightly more complicated example that calculates factorials. The program converts the value selected in the NumericUpDown
control named numberNumericUpDown
into a long integer and saves it in variable n
. It initializes the variable factorial
to 1 and then uses a loop to multiply factorial
by each of the numbers between 2 and n
. The result is 1 * 2 * 3 * … * n, which is n!
:
// Get the input value N.
long n = (long)numberNumericUpDown.Value;
// Calculate N!.
long factorial = 1;
for (int i = 2; i <= n; i++)
{
checked
{
factorial *= i;
}
}
// Display the result.
resultTextBox.Text = factorial.ToString();
You may recall that Lesson 16 used code to calculate Fibonacci numbers, and in that lesson's Exercise 1 you calculated factorials. Those programs used 20 lines of code to calculate and store 20 values that the program then used as a kind of lookup table.
The factorial code shown here uses a lot less code. It doesn't require a large array to hold values. It also doesn't require that you know ahead of time how many values you might need to calculate (20 for the earlier programs), although the factorial function grows so quickly that this program can only calculate values up to 20! before the result won't fit in a long
.
A foreach
loop executes a block of code once for each item in an array or list. The syntax of the foreach
loop is as follows:
foreach (variableDeclaration in items)
{
statements…
}
Where:
variableDeclaration
declares the looping variable. Its type must be the same as the items in the array or list.items
is the array or list of items over which you want to loop.statements
are the statements that you want the loop to execute.For example, the following code calculates the average of the test scores stored in the ListBox
named scoresListBox
. Note that the ListBox
must contain integers or something the program can implicitly convert into an integer or else the program will crash:
// Add up the values.
int total = 0;
foreach (int value in valuesListBox.Items)
{
total += value;
}
// Calculate the average.
float average = (float)total / valuesListBox.Items.Count;
The code creates a variable named total
and sets it equal to 0. It then loops through the items in the ListBox
, adding each value to total
.
The code finishes by dividing the total by the number of items in the ListBox
.
A while
loop executes as long as some condition is true. The syntax for a while
loop is as follows:
while (condition)
{
statements…
}
Where:
condition
is a boolean expression. The loop executes as long as this expression is true.statements
are the statements that you want the loop to execute.For example, the following code calculates a number's prime factors:
// Find the number's prime factors.
private void factorButton_Click(object sender, EventArgs e)
{
// Get the input number.
long number = long.Parse(numberTextBox.Text);
// Find the factors.
string result = "1";
// Consider factors between 2 and the number.
for (long factor = 2; factor <= number; factor++)
{
// Pull out as many copies of this factor as possible.
while (number % factor == 0)
{
result += " x " + factor.ToString();
number = number / factor;
}
}
// Display the result.
resultTextBox.Text = result;
}
The code starts by getting the user's input number. It builds a result string and initializes it to “1.”
Next the code uses a for
loop to consider the numbers between 2 and the user's number as possible factors.
For each of the possible factors, it uses a while
loop to remove that factor from the number. As long as the factor divides evenly into the remaining number, the program adds the factor to the result and divides the user's number by the factor.
The code finishes by displaying its result.
A do
loop is similar to a while
loop except it checks its stopping condition at the end of the loop instead of at the beginning. The syntax of a do
loop is as follows:
do
{
statements…
} while (condition);
Where:
statements
are the statements that you want the loop to execute.condition
is a boolean expression. The loop continues to execute as long as this expression is true.The following code uses a do
loop to calculate the greatest common divisor (GCD) of two numbers, the largest number that divides them both evenly:
// Calculate GCD(A, B).
private void calculateButton_Click(object sender, EventArgs e)
{
// Get the input values.
long a = long.Parse(aTextBox.Text);
long b = long.Parse(bTextBox.Text);
// Calculate the GCD.
long remainder;
do
{
remainder = a % b;
if (remainder != 0)
{
a = b;
b = remainder;
}
} while (remainder > 0);
resultTextBox.Text = b.ToString();
}
It's important that any loop eventually ends, and in this code it's not completely obvious why that happens. It turns out that each time through the loop (with the possible exception of the first time), a
and b
get smaller. If you step through a few examples, you'll be able to convince yourself.
If the loop runs long enough, b
eventually reaches 1. At that point b
must evenly divide a
no matter what a
is so the loop ends. If b
does reach 1, then 1 is the greatest common divisor of the user's original numbers and those numbers are called relatively prime.
The break
and continue
statements change the way a loop works.
The break
statement makes the code exit the loop immediately without executing any more statements inside the loop.
For example, the following code searches the selected items in a ListBox
for the value Carter
. If it finds that value, it sets the boolean variable carterSelected
to true
and breaks out of the loop. If the ListBox
has many selected items, breaking out of the loop early may let the program skip many loop iterations and save some time:
// See if Carter is one of the selected names.
bool carterSelected = false;
foreach (string name in namesListBox.SelectedItems)
{
if (name == "Carter")
{
carterSelected = true;
break;
}
}
MessageBox.Show(carterSelected.ToString());
The continue
statement makes a loop jump to its looping statement early, skipping any remaining statements inside the loop after the continue
statement.
For example, the following code uses a foreach
loop to display the square roots of the numbers in an array. The Math.Sqrt
function cannot calculate the square root of a negative number so, to avoid trouble, the code checks each value. If it finds a value less than zero, it uses the continue
statement to skip the rest of that trip through the loop so it doesn't try to take the number's square root. It then continues with the next number in the array:
// Display square roots.
float[] values = { 4, 16, -1, 60, 100 };
foreach (float value in values)
{
if (value < 0) continue;
Console.WriteLine(string.Format("The square root of {0} is {1:0.00}",
value, Math.Sqrt(value)));
}
The following text shows this program's results:
The square root of 4 is 2.00
The square root of 16 is 4.00
The square root of 60 is 7.75
The square root of 100 is 10.00
In this Try It, you make the simple login form shown in Figure 19.1. When the program's startup form loads, it enters a loop that makes it display this form until the user enters the correct username and password or clicks Cancel.
In this lesson, you:
Load
event handler, create an instance of the login dialog. Then enter a while
loop that displays the dialog and doesn't stop until the user enters a username and password that match values in the code. If the user clicks Cancel, close the main form.tryingToLogin
to control the loop. Initialize it to true
before the loop and set it to false
when the user either cancels or enters the right username and password.TextBox
's PasswordChar
property to X
.Load
event handler, create an instance of the login dialog. Then enter a while
loop that displays the dialog and doesn't stop until the user enters a username and password that match values in the code. If the user clicks Cancel, close the main form and break out of the loop.
// Make the user log in.
private void Form1_Load(object sender, EventArgs e)
{
// Create a LoginForm.
LoginForm frm = new LoginForm();
// Repeat until the user successfully logs in.
bool tryingToLogin = true;
while (tryingToLogin)
{
// Display the login dialog and check the result.
if (frm.ShowDialog() == DialogResult.Cancel)
{
// The user gives up. Close and exit the loop.
this.Close();
tryingToLogin = false;
}
else
{
// See if the user entered valid values.
if ((frm.usernameTextBox.Text == "User") &&
(frm.passwordTextBox.Text == "Secret"))
{
// Login succeeded. Stop trying to log in.
tryingToLogin = false;
}
else
{
// Login failed. Display a message and
// let the loop continue.
MessageBox.Show("Invalid username and password.");
}
}
}
// If we get here, we're done trying to log in.
}
Fibonacci(0) = 0
Fibonacci(1) = 1
Fibonacci(N) = Fibonacci(N - 1) + Fibonacci(N - 2)
Hint: Use a loop. Define variables fibo1
, fibo2
, and fiboN
outside the loop. Inside the loop, make the variables hold Fibonacci(N - 1)
, Fibonacci(N - 2)
, and Fibonacci(N)
. (To test your code, Fibonacci(10)
= 55 and Fibonacci(20)
= 6,765.)
ListBox
. After adding each score, display the minimum, maximum, and average values. (Hint: Before you start the loop, initialize minimum
and maximum
variables to the value of the first score. Then loop through the list revising the variables as needed.)Console
class to display the items and their values in the Output window as a semicolon-separated list similar to the following:
**********
Pencil;$0.10;12;$1.20;
Pen;$0.25;12;$3.00;
Notebook;$1.19;3;$3.57;
**********
Hint: The ListView
control's Items
property is a collection of ListViewItem
objects. Loop through that collection to get information about each row.
Hint: Each ListViewItem
has a SubItems
property that is a collection of ListViewItem.ListViewSubItem
objects. For each row, loop through the item's subitem collection to get the values for that row. Use Console.Write
to add data to the Console window without adding a carriage return.
foreach
loop to loop through the letters. Inside that loop, use another loop to loop through the letters again. After four depths of nested loops, concatenate the looping variables to get the word.)
PictureBox
controls to hold the ball images.Vx
and Vy
to hold the balls' velocities.Balls
to hold references to the balls' PictureBox
es.Balls
array and give the balls random initial locations and velocities.Timer
's Tick
event handler, loop through the Balls
array and update the balls' locations and velocities.PictureBox
es when they overlap each other. Copy that program and fix it by following these steps:
PictureBox
es.NumBalls = 4
, BallWidth = 40
, and BallHeight = 40
.X
and Y
arrays to hold the balls' locations. When the form loads, initialize those arrays with random positions.Timer
's Tick
event fires, update the balls' locations and velocities as before (except using the X
and Y
arrays instead of the PictureBox
controls' Left
and Top
properties).Refresh
method to make it redraw itself.Paint
event handler to draw the balls:
// Draw the balls.
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode =
System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
for (int ball = 0; ball < NumBalls; ball++)
{
e.Graphics.FillEllipse(Brushes.Red,
X[ball], Y[ball], BallWidth, BallHeight);
e.Graphics.DrawEllipse(Pens.Black,
X[ball], Y[ball], BallWidth, BallHeight);
}
}
DoubleBuffered
property to True
.BallBrushes
to hold Brush
objects.Load
event handler:
brushes
to hold Brush
objects. Initialize it to a selection of standard brushes such as Brushes.Pink
and Brushes.LightGreen
.brushes
array:
BallBrushes[ball] = brushes[rand.Next(0, brushes.Length)];
Hints:
List<Point>
to keep track of the positions of the circles.Timer
ticks:
Insert
method to insert a new Point
at the beginning of the list for the new position.RemoveAt
method to remove the last position from the list.Refresh
method to make it redraw itself.Paint
event handler loop through the list and draw the worm's circles.Brush
objects.Point
objects (List<Point>[]
).List<Point>[]
, you need to initialize it with the new
keyword.List<Point>[]
, you need to initialize it with the new
keyword.HighScoreForm
:
// Display the high scores.
HighScoresForm highScoresForm = new HighScoresForm();
highScoresForm.nameLabel0.Text = HighScoreNames[0];
highScoresForm.nameLabel1.Text = HighScoreNames[1];
highScoresForm.nameLabel2.Text = HighScoreNames[2];
highScoresForm.nameLabel3.Text = HighScoreNames[3];
highScoresForm.nameLabel4.Text = HighScoreNames[4];
highScoresForm.scoreLabel0.Text = HighScores[0].ToString();
highScoresForm.scoreLabel1.Text = HighScores[1].ToString();
highScoresForm.scoreLabel2.Text = HighScores[2].ToString();
highScoresForm.scoreLabel3.Text = HighScores[3].ToString();
highScoresForm.scoreLabel4.Text = HighScores[4].ToString();
Modify the program so it uses two for
loops instead. (Hints: Use two arrays holding the form's controls. You'll have to make the change in two places.)