In the previous lesson you learned how to make your program take decisions with if/else, but those decisions were only evaluated once. In BiblioTechConsole, though, almost no real task consists of looking at a single piece of data: you need to go through the entire book catalog to display it, count how many copies are available, or search for one by title until it's found (or confirm it doesn't exist). Repeating an action a fixed or an unknown number of times is the job of loops. In this lesson you'll learn the four loop forms C# offers — for, while, do-while, and foreach — and how to control them with break and continue.

Content

  1. The for loop
  2. The while loop
  3. The do-while loop
  4. The foreach loop
  5. Comparison: when to use each one
  6. break and continue
  7. Nested loops
  8. Full application: listing, counting, and searching the catalog

  1. The for loop

The for loop is ideal when you know in advance (or can calculate) how many times an action must be repeated, typically because you're iterating over a collection by index.

string[] bookTitles = { "One Hundred Years of Solitude", "Hopscotch", "Ficciones", "The Aleph" };

for (int i = 0; i < bookTitles.Length; i++)
{
    Console.WriteLine($"{i + 1}) {bookTitles[i]}");
}

The for header has three parts separated by ;:

Part Example When it runs
Initialization int i = 0 Once, before starting
Condition i < bookTitles.Length Before each iteration; if false, the loop ends
Increment (or update) i++ After each iteration

Using bookTitles.Length in the condition, instead of a fixed number like 4, keeps the loop from breaking (or failing) if the array's size changes later on.

You can also iterate in reverse, which is useful when elements are going to be removed from a collection while iterating over it (a topic that will be expanded on with List<T> in Module 4):

for (int i = bookTitles.Length - 1; i >= 0; i--)
{
    Console.WriteLine(bookTitles[i]);
}

  1. The while loop

while repeats a block while a condition is true, evaluating it before each iteration. It's the natural choice when you don't know in advance how many times the action needs to be repeated.

int index = 0;
int availableBooks = 0;
bool[] availability = { true, false, true, true };

while (index < availability.Length)
{
    if (availability[index])
    {
        availableBooks++;
    }

    index++;
}

Console.WriteLine($"Available books: {availableBooks}");

A typical while use case is waiting for valid user input, where you don't know how many attempts it will take:

string? input = null;
int bookPages = 0;
bool validInput = false;

while (!validInput)
{
    Console.Write("Enter the number of pages in the book: ");
    input = Console.ReadLine();
    validInput = int.TryParse(input, out bookPages);

    if (!validInput)
    {
        Console.WriteLine("Invalid input, try again.");
    }
}

Console.WriteLine($"Page count recorded: {bookPages}");

Watch out: if a while condition never becomes false, the loop runs forever (an infinite loop). There must always be, inside the block, some statement that moves the condition toward its end (in the first example, index++).

  1. The do-while loop

do-while is almost identical to while, with one key difference: the condition is evaluated after the block runs, so the block always executes at least once, even if the condition was already false from the start.

int attempts = 0;
bool loanRegistered = false;

do
{
    attempts++;
    Console.WriteLine($"Attempt number {attempts} to register the loan...");

    // We simulate the registration succeeding on the second attempt
    loanRegistered = attempts >= 2;

} while (!loanRegistered);

Console.WriteLine("Loan registered successfully.");

Note the syntax: the while keyword and its condition go after the closing brace, and the line ends with ;. This pattern fits interactive menus well, where the menu must be displayed at least once before checking whether the user wants to exit.

  1. The foreach loop

foreach iterates over all the elements of a collection (an array, or any type that is "iterable", as you'll see in more detail in Module 4 with collections), without needing to manually manage an index or a stopping condition.

string[] bookTitles = { "One Hundred Years of Solitude", "Hopscotch", "Ficciones", "The Aleph" };

foreach (string title in bookTitles)
{
    Console.WriteLine(title);
}

foreach is more readable than for when you don't need the index, but it has an important limitation: the iteration variable (title in the example) is read-only inside the loop; you cannot use foreach to modify the array's elements directly through it.

int[] availableCopies = { 2, 0, 1, 3 };

// foreach (int copies in availableCopies)
// {
//     copies++; // Compile error: cannot assign to the iteration variable
// }

// If you need to modify the array, use a for loop with an index:
for (int i = 0; i < availableCopies.Length; i++)
{
    availableCopies[i]++;
}

  1. Comparison: when to use each one

Loop Use it when... It evaluates the condition...
for You know (or can calculate) the number of iterations, or you need the index Before each iteration
while You don't know how many iterations will be needed, and it could be zero Before each iteration
do-while Like while, but the block must run at least once After each iteration
foreach You just need to go through all the elements, without an index or modifying them Before each iteration (internally)

In practice, for and foreach cover the vast majority of traversals over arrays and collections in BiblioTechConsole; while and do-while are reserved for situations where the stopping condition depends on something other than "going through a full collection", such as validating user input or waiting for an external condition to be met.

  1. break and continue

Inside any loop, two keywords let you alter the normal iteration flow:

  • break: ends the loop immediately, without evaluating any further iterations.
  • continue: skips the rest of the loop body in the current iteration and moves straight to the next one.
string[] bookTitles = { "One Hundred Years of Solitude", "Hopscotch", "Ficciones", "The Aleph" };
string searchedTitle = "Ficciones";

for (int i = 0; i < bookTitles.Length; i++)
{
    if (bookTitles[i] == searchedTitle)
    {
        Console.WriteLine($"Found at position {i}.");
        break; // no need to keep scanning the array
    }
}
bool[] availability = { true, false, true, true, false };

for (int i = 0; i < availability.Length; i++)
{
    if (!availability[i])
    {
        continue; // skip books that aren't available
    }

    Console.WriteLine($"Book at position {i}: available");
}

break also works inside while, do-while, and foreach; so does continue.

  1. Nested loops

A loop can contain another loop inside it. This is common when working with two-dimensional arrays (which you already saw in the arrays lesson), for example, to represent a map of shelves and shelf levels in BiblioTech:

string[,] shelfMap = new string[2, 3]
{
    { "Fiction", "Fiction", "Non-fiction" },
    { "Poetry", "Children's", "Children's" }
};

for (int shelf = 0; shelf < 2; shelf++)
{
    for (int shelfLevel = 0; shelfLevel < 3; shelfLevel++)
    {
        Console.WriteLine($"Shelf {shelf}, level {shelfLevel}: {shelfMap[shelf, shelfLevel]}");
    }
}

The outer loop iterates over shelves; for each shelf, the inner loop iterates over its levels. In total, 2 × 3 = 6 iterations run. Keep in mind that break and continue only affect the innermost loop they're in; if you need to exit both loops at once, a simple technique is to use a boolean variable as a flag:

bool found = false;

for (int shelf = 0; shelf < 2 && !found; shelf++)
{
    for (int shelfLevel = 0; shelfLevel < 3; shelfLevel++)
    {
        if (shelfMap[shelf, shelfLevel] == "Non-fiction")
        {
            Console.WriteLine($"Genre found at shelf {shelf}, level {shelfLevel}");
            found = true;
            break;
        }
    }
}

  1. Full application: listing, counting, and searching the catalog

Let's combine everything above into three typical operations on the BiblioTechConsole catalog: listing the titles, counting how many are available, and searching for one by title.

string[] bookTitles = { "One Hundred Years of Solitude", "Hopscotch", "Ficciones", "The Aleph", "Pedro Páramo" };
bool[] availability = { true, false, true, true, false };

// 1) List all titles with their status (foreach won't work here because we need the index)
Console.WriteLine("=== BiblioTech Catalog ===");
for (int i = 0; i < bookTitles.Length; i++)
{
    string status = availability[i] ? "Available" : "On loan";
    Console.WriteLine($"{i + 1}) {bookTitles[i]} - {status}");
}

// 2) Count how many books are available (foreach: we don't need the index)
int totalAvailable = 0;
foreach (bool isAvailable in availability)
{
    if (isAvailable)
    {
        totalAvailable++;
    }
}
Console.WriteLine($"Total available books: {totalAvailable}");

// 3) Search for a specific title and stop as soon as it's found
string searchedTitle = "Ficciones";
bool found = false;

for (int i = 0; i < bookTitles.Length; i++)
{
    if (bookTitles[i] == searchedTitle)
    {
        Console.WriteLine($"'{searchedTitle}' found at position {i + 1}, available: {availability[i]}");
        found = true;
        break;
    }
}

if (!found)
{
    Console.WriteLine($"'{searchedTitle}' is not in the catalog.");
}

This snippet already combines a for (when the index matters), a foreach (when only the value matters), and break (to stop a search as soon as the result is found): three deliberate decisions, not arbitrary ones.

Common Mistakes and Tips

  • Infinite loops: forgetting to update the control variable in a while (for example, not incrementing the index) leaves the program hanging forever. Always double-check that the condition can eventually become false.
  • Overflowing an array's index: in a for loop, using <= instead of < against array.Length causes access to a position that doesn't exist and throws an exception at runtime (you'll see this in detail in the last lesson of the module).
  • Trying to modify the foreach variable: as shown, this doesn't compile. If you need to modify the elements, use for with an index.
  • Using for when foreach is clearer (and vice versa): if you don't need the index, prefer foreach; it improves readability and reduces the risk of indexing mistakes.
  • Confusing break with continue: break exits the entire loop; continue only skips to the next iteration. Using the wrong one often produces incomplete results or loops that end too early.
  • Nesting loops unnecessarily: before nesting two loops, check whether the problem can be solved with a single pass; nesting multiplies the number of iterations (and the running time) and adds complexity.

Exercises

  1. Given the array int[] availableCopies = { 3, 0, 5, 2, 0 };, use a for loop to iterate over it and print to the console how many positions have 0 available copies (out-of-stock books).

  2. Use a while loop to simulate validating an administrator password: start from string attempt = ""; and a simulated list of valid attempts string[] simulatedAttempts = { "1234", "abcd", "bibliotech" }; (walk this array with an increasing index on each pass of the while, as if they were successive user attempts). The loop must stop as soon as attempt equals "bibliotech", displaying how many attempts it took.

  3. Given the array of titles string[] bookTitles = { "One Hundred Years of Solitude", "Hopscotch", "Ficciones", "The Aleph" };, iterate over the array with foreach and, using continue, print to the console only the titles that have more than 9 characters.

Solutions

int[] availableCopies = { 3, 0, 5, 2, 0 };
int outOfStockBooks = 0;

for (int i = 0; i < availableCopies.Length; i++)
{
    if (availableCopies[i] == 0)
    {
        outOfStockBooks++;
    }
}

Console.WriteLine($"Out-of-stock books: {outOfStockBooks}");

The result is 2, corresponding to positions 1 and 4 (indices starting at 0).

string[] simulatedAttempts = { "1234", "abcd", "bibliotech" };
string attempt = "";
int index = 0;
int attemptCount = 0;

while (attempt != "bibliotech")
{
    attempt = simulatedAttempts[index];
    attemptCount++;
    index++;
}

Console.WriteLine($"Correct password found after {attemptCount} attempt(s).");

The result shows 3 attempts, because "bibliotech" occupies the third position in the simulated array (index 2). Notice that the while condition is evaluated before each pass, so the last check (attempt != "bibliotech", now false) is the one that stops the loop without running an extra pass.

string[] bookTitles = { "One Hundred Years of Solitude", "Hopscotch", "Ficciones", "The Aleph" };

foreach (string title in bookTitles)
{
    if (title.Length <= 9)
    {
        continue;
    }

    Console.WriteLine(title);
}

Only "One Hundred Years of Solitude" (29 characters) is printed, since it's the only title with more than 9 characters. "Hopscotch", "Ficciones", and "The Aleph" are all exactly 9 characters long, so the continue skips all three without printing anything for them. This is a good exercise to check with Console.WriteLine(title.Length) how the result changes if you use < instead of <=.

Conclusion

In this lesson you've learned to repeat actions with for, while, and do-while, to iterate over collections easily with foreach, to control a loop's flow with break and continue, and to combine nested loops when data has more than one dimension. With these tools, BiblioTechConsole can now list its full catalog, count available books, and search for specific titles without repeating code by hand.

So far, to classify something (like the status of a loan) you've used chains of if/else if/else. In the next lesson, Switch Statements, you'll learn a clearer alternative for when you need to compare the same variable against many possible values, for example, to classify a book by its category or genre within BiblioTech.

© Copyright 2026. All rights reserved