C# from the ground up

Loops: for, while and foreach

Repeating work a counted number of times with for, repeating while a condition holds with while, and walking a collection with foreach.

Part 5 of 13

Intermediate15 min readBack to Courses

Why a loop exists

A loop repeats a block of work without you writing the block out again. It is not about saving typing: it is about there being one copy of the work, so a change happens in one place and the count comes from the data rather than from your memory.

Every loop needs a way to finish. The three shapes below differ only in how they decide to stop, and if a loop can never stop, the program runs until the sandbox cuts it off.

for: a counted number of times

A for loop puts the three moving parts in one line: where the counter starts, the test that keeps it going, and the step at the end of each turn. for (var i = 0; i < 5; i++) reads as "start at nought, carry on while i is under five, add one each time". The counter belongs to the loop, so it is declared in it.

Counting from nought is the habit worth keeping, because indexes start there too. A while loop is the simpler relative: it tests a condition before each turn and knows nothing about a counter, so something inside the block has to change that condition. Reading a condition that never changes is how you spot an endless loop.

foreach: every item, one at a time

foreach (var name in names) walks a collection and hands you each item. There is no counter to get wrong and no index to go out of range, so it is the first choice for reading everything in an array or a list. It also walks text, handing you one char at a time, and it walks a dictionary as pairs of key and value. It asks the collection for an enumerator rather than for an index, so it works on anything that can be walked in order, including the result of a query.

Two statements change the flow without ending the whole repetition. break leaves the loop at once, and continue skips the rest of this turn and moves to the next one. Both are worth using deliberately, and a loop that uses either is usually easier to read with a comment saying why.

The three loops, and leaving early

using System;

class Program
{
    static void Main()
    {
        // A counted loop: start, test, step.
        for (var i = 1; i <= 3; i++)
        {
            Console.WriteLine("Round " + i);
        }

        // while: the block has to change the condition.
        var left = 2;
        while (left > 0)
        {
            Console.WriteLine("Left " + left);
            left = left - 1;
        }

        // Every item of a collection, without a counter.
        var names = new[] { "tea", "cake", "jam" };
        foreach (var name in names)
        {
            Console.WriteLine(name);
        }

        // Leave the loop, or skip just this turn.
        foreach (var name in names)
        {
            if (name == "cake") { continue; }
            if (name == "jam") { break; }
            Console.WriteLine("wanted " + name);
        }
    }
}

A sum, a list and a countdown

Run it and read the nine lines. Then make the for loop count to 10 instead of 5, and add a line inside the foreach that prints the length of each name.

Output
Click "Run" to execute your code

Choose the loop by how it stops: a count is a for, a condition is a while, and everything in a collection is a foreach. The reading then matches the intent.

A for loop is a while loop with the parts moved

for (var i = 0; i < 3; i++) does the same work as a while with the counter set before it and the step at the bottom. The for form is not faster; it puts the three parts where a reader can see them at once, which is why a counted repetition is easier to trust in that shape.

The counter is a local variable. It is created when the loop starts, tested before every turn, and stepped after the body. Understanding that order explains the usual off-by-one: a loop that starts at zero and tests < Length runs exactly as many times as there are items.

Nested loops, and what break leaves

A loop inside a loop runs the inner one completely for every turn of the outer one. A table is the natural example: the outer loop picks a row, and the inner loop fills its columns. The work is the product of the two counts, which is worth noticing before the counts get large. The inner loop starts again from its first value on every outer turn, so a counter declared inside the outer body is a fresh variable each time and the reset needs no code.

break leaves only the loop it is written in. Inside a nested pair it exits the inner one and the outer one carries on. If you need to leave both, use a condition the outer loop tests, or return from a method that contains them.

Three loop mistakes: starting at one but testing < Length, which skips the last item; changing the collection inside a foreach, which throws rather than quietly misbehaving; and writing a while whose body never changes the condition, so the program runs until the sandbox stops it.

Try these next

  • Print a three-by-three grid of numbers with two nested for loops.
  • Sum the even numbers from 1 to 10 with a for loop and continue, then again without continue.
  • Write a while countdown from five, then delete the line that decreases the counter and predict what happens before you run it.

A table built by two loops

Run it and read the three rows. Then make the table four columns wide and five rows tall, and add a heading line above it.

Output
Click "Run" to execute your code

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

What are the three parts inside the parentheses of a for loop?