Loops

C# · Lesson 11 of 31 6 min read Updated

Computers are good at doing the same thing many times without getting bored: printing a table, adding up a thousand bills, asking again until the input is valid. A loop repeats a block of code while a condition is true or once for each item in a collection.

C# has four loops: for, while, do-while and foreach. In this lesson you will learn when to use each one, how break and continue change the flow, and how to avoid the two classic loop bugs.

The for loop

Use for when you know how many times to repeat, usually with a counter. Its header has three parts separated by semicolons: start, condition and step.

C#
for (int i = 1; i <= 5; i++)
{
    Console.WriteLine($"17 x {i} = {17 * i}");
}

for (int countdown = 10; countdown > 0; countdown -= 3)
{
    Console.Write($"{countdown} ");
}
Console.WriteLine("Lift-off!");
Output
17 x 1 = 17
17 x 2 = 34
17 x 3 = 51
17 x 4 = 68
17 x 5 = 85
10 7 4 1 Lift-off!

The loop runs like this: set i = 1; check i <= 5; if true, run the block; do the step i++; check again; and so on. When the condition becomes false, the loop ends. The step can be anything, such as countdown -= 3.

The variable i exists only inside the loop. After the closing brace, using i is a compile error, which keeps loop counters from leaking into the rest of your code.

The while loop

Use while when you do not know in advance how many times to repeat. It checks the condition before every pass, so the body may run zero times.

C#
decimal savings = 0m;
decimal monthly = 1500m;
int months = 0;

while (savings < 10000m)
{
    months++;
    savings += monthly;
}

Console.WriteLine($"Ananya reaches her laptop goal in {months} months.");
Console.WriteLine($"Saved: Rs {savings}");
Output
Ananya reaches her laptop goal in 7 months.
Saved: Rs 10500

Something inside the loop must eventually make the condition false. Here savings grows every month. If you forgot savings += monthly;, the loop would run forever: an infinite loop. Press Ctrl+C in the terminal to stop a program stuck like that.

The do-while loop

A do-while loop checks its condition after each pass, so the body always runs at least once. That makes it perfect for menus.

C#
string? choice;
int balance = 5000;

do
{
    Console.WriteLine("1. Check balance   2. Exit");
    Console.Write("Choose: ");
    choice = Console.ReadLine();

    if (choice == "1")
    {
        Console.WriteLine($"Balance: Rs {balance}");
    }
} while (choice != "2");

Console.WriteLine("Thank you for banking with us.");
Output
1. Check balance   2. Exit
Choose: 1
Balance: Rs 5000
1. Check balance   2. Exit
Choose: 2
Thank you for banking with us.

Note the semicolon after while (choice != "2"): a do-while is the only loop that ends with one.

The foreach loop

foreach visits every item of an array or collection, in order, without any counter. It is the loop you will write most often in real C#.

C#
int[] balls = [1, 0, 4, 0, 6, -1, 2];
int runs = 0;

foreach (int ball in balls)
{
    if (ball == -1)
    {
        Console.WriteLine("Wicket! Innings over.");
        break;
    }
    if (ball == 0)
    {
        continue; // dot ball: nothing to add
    }
    runs += ball;
    Console.WriteLine($"Scored {ball}, total {runs}");
}

Console.WriteLine($"Final score: {runs}");
Output
Scored 1, total 1
Scored 4, total 5
Scored 6, total 11
Wicket! Innings over.
Final score: 11

Two keywords control any loop:

  • break leaves the loop immediately. The last ball, 2, is never read.
  • continue skips the rest of this pass and moves on to the next item.

The loop variable in foreach is read-only: ball = 5; inside the loop is a compile error. Also, you must not add or remove items from a list while a foreach is walking through it; that throws InvalidOperationException: Collection was modified; enumeration operation may not execute.

Nested loops

A loop can contain another loop. The inner loop runs completely for every single pass of the outer loop. Here is a small seat map for a bus:

C#
string[] rows = ["A", "B", "C"];

foreach (string row in rows)
{
    for (int seat = 1; seat <= 4; seat++)
    {
        Console.Write($"{row}{seat} ");
    }
    Console.WriteLine();
}
Output
A1 A2 A3 A4 
B1 B2 B3 B4 
C1 C2 C3 C4 

Three rows times four seats gives twelve inner passes. Nested loops are common for tables, grids and comparing every item with every other item, but they get slow quickly: 1,000 outer passes times 1,000 inner passes is a million.

Loops and ranges of numbers

For a simple run of numbers, Enumerable.Range(start, count) produces them for a foreach, and LINQ methods such as Sum can often replace a loop entirely. You will learn LINQ in LINQ.

C#
int total = 0;
foreach (int n in Enumerable.Range(1, 100))
{
    total += n;
}
Console.WriteLine(total);
Console.WriteLine(Enumerable.Range(1, 100).Sum());
Output
5050
5050

Common mistakes

  • Off-by-one errors. For an array of length 5, valid indexes are 0 to 4. for (int i = 0; i <= arr.Length; i++) reads arr[5] and throws IndexOutOfRangeException. Use < with Length.
  • Infinite loops. The condition never becomes false because the variable is never updated. Check that every while changes something it tests.
  • A semicolon after the header. for (int i = 0; i < 5; i++); runs an empty body five times, then the block below runs once.
  • Changing a collection inside foreach. Collect the changes and apply them after the loop, or loop backwards with for.
  • Using while where foreach fits. Manual counters are easy to get wrong; let foreach do the work.

Key points

  • for (start; condition; step) repeats with a counter.
  • while checks before each pass; do-while checks after, so it runs at least once.
  • foreach visits each item of an array or collection; its variable is read-only.
  • break exits a loop; continue jumps to the next pass.
  • Inner loops run fully for each pass of the outer loop.
  • Watch for off-by-one errors and conditions that never become false.

Exercise

A school in Jaipur collects fees in instalments. Write a program with an array of payments [2500, 3000, 0, 4500, 3000] (0 means a month was missed). Use a loop to print each month's payment, skip missed months with continue after printing "Month 3: missed", and stop with break as soon as the total reaches at least Rs 10,000. Finally, print the total collected.

Show solution
C#
int[] payments = [2500, 3000, 0, 4500, 3000];
int total = 0;

for (int i = 0; i < payments.Length; i++)
{
    int month = i + 1;
    if (payments[i] == 0)
    {
        Console.WriteLine($"Month {month}: missed");
        continue;
    }

    total += payments[i];
    Console.WriteLine($"Month {month}: Rs {payments[i]} (total Rs {total})");

    if (total >= 10000)
    {
        Console.WriteLine("Fee target reached.");
        break;
    }
}

Console.WriteLine($"Collected: Rs {total}");
Output
Month 1: Rs 2500 (total Rs 2500)
Month 2: Rs 3000 (total Rs 5500)
Month 3: missed
Month 4: Rs 4500 (total Rs 10000)
Fee target reached.
Collected: Rs 10000

A for loop fits because the month number comes from the index. continue skips the adding for the missed month, and break stops before month 5 is read.

Quick Quiz

1. How many times does for (int i = 0; i < 4; i++) run its body?

2. Which loop always runs its body at least once?

3. What does continue do inside a loop?

4. What happens if you remove items from a List while a foreach loops over it?

Learn C# with a mentor

Free lessons get you started. Our Programming & Software Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call