Start learning free

LESSON 112 OF 347 · FOR LOOPS - LOOPING OVER ARRAYS

Infinite Loops in C#: Changing a List While Using foreach

Lesson Read the full explanation

We also use for loops to iterate over sequences like strings and arrays. We can run into problems when we manipulate a sequence while iterating on it.

For example, if we append elements to an array while iterating on it:

List<string> fruits = new List<string> { "banana", "orange" };

foreach (var fruit in fruits) {
  fruits.Add("kivi");
}

Every time we enter this loop, we add a kivi item to the end of the list that we are iterating through, so we would never make it to the end of the list: it would keep growing forever.

C# stops this for us: after the first item is added, the loop's next step throws an InvalidOperationException ("Collection was modified; enumeration operation may not execute").

Changing a collection while looping over it is always a bug. And when nothing stops the loop, the collection keeps growing forever: an infinite loop. As programmers, we want to make sure we never write infinite loops, as they make our program run forever and completely unusable.


Assignment
Follow the Coding Tutorial and let's practice with infinite for loops!


Hint
Look at the examples above if you get stuck.


STEP 1 OF 1
    Ctrl + Enter to check

    C# · FOR LOOPS - LOOPING OVER ARRAYS

    Infinite For Loops 2 in C#, explained

    What goes wrong

    A loop is infinite when its stopping condition never becomes true. With foreach, the loop stops when it runs out of elements. So if every pass adds an element to the collection being walked, the end keeps moving away and the loop could never finish.

    Here is the classic mistake, using a List<string>:

    List<string> fruits = new List<string> { "banana", "orange" };
    
    foreach (var fruit in fruits) {
        fruits.Add("kiwi");
    }
    

    Each pass appends a "kiwi" to the list that the loop is reading from. If C# let this run, the list would grow forever.

    What C# does about it

    C# catches this for you. A foreach over a List<T> checks, before it moves to the next element, whether the list has changed since the loop started. If it has, the loop throws an InvalidOperationException with this message:

    Collection was modified; enumeration operation may not execute.
    

    In the example above, the first pass runs and adds one "kiwi". The loop then tries to continue, sees that the list was modified, and throws. The program stops with an error instead of running forever.

    This protection belongs to foreach over the standard collections. Other loops have none. A while loop or an index-based for loop that keeps adding to a list while it compares an index with list.Count can really run until memory runs out. The safe rule is the same everywhere: do not add to or remove from a collection while looping over it.

    An array can't grow at all, because its length is fixed when it is created. That's why growing collections in C# use List<T>, and why the problem appears with lists and not with arrays.

    The exercise, step by step

    The editor has two lists. students holds Andy, Mary and Alex. newStudents holds Ana, Alexa and Mike. The goal is to add every new student to the end of students, so that the program has Andy, Mary, Alex, Ana, Alexa and Mike.

    1. Find which list the loop walks. The header is foreach (var student in newStudents), so newStudents is the list being read.
    2. Find which list the body changes. The buggy body calls newStudents.Add(student). It adds to the same list the loop is reading, which triggers the exception, and it never puts anything into students.
    3. Change the target of Add. Read from one list and write to the other: call students.Add(student).
    List<string> students = new List<string>(new[] {"Andy", "Mary", "Alex"});
    List<string> newStudents = new List<string>(new[] {"Ana", "Alexa", "Mike"});
    
    foreach (var student in newStudents) {
        students.Add(student);
    }
    

    Now the loop reads newStudents without changing it, so the check never fails. It makes three passes, and each pass appends the current name to students. The result is the six names in the expected order. Printing them with string.Join(", ", students) would show Andy, Mary, Alex, Ana, Alexa, Mike.

    Two C# details in this code

    • var student lets the compiler work out the type from the list. Because newStudents is a List<string>, student is a string.
    • new List<string>(new[] {...}) builds a list from an array of the starting values. A collection initializer, new List<string> { "Andy", "Mary" }, does the same job.

    If you really need to change the list

    Sometimes you want to add or remove items based on the list's own contents. Two safe patterns:

    • Collect the changes first. Walk the list, put the items you want to add into a second list, and apply that second list after the loop ends.
    • Loop over a copy. foreach (var item in new List<string>(original)) walks a snapshot, so changing original in the body is allowed.

    Key takeaways

    • An infinite loop never reaches its stopping condition, so the program can't finish.
    • Adding to a list while a foreach reads it makes C# throw InvalidOperationException.
    • Read from one collection and write to another, or collect changes and apply them after the loop.
    • Check which collection the loop header names, and which one the body modifies.
    Keep going with C#About 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free