foreach loop in C#

The foreach loop in C# is used to iterate through each item in a collection. It is cleaner than a for loop when you do not need an index and simply want to process every element. It works with arrays, lists, strings, dictionaries, sets, and any type that supports enumeration.

In real C# programming, foreach is often the most readable loop for collection traversal. It says exactly what the code means: take each item one by one and run the loop body for that item.


What Is foreach loop in C#?

A foreach loop reads each item from a collection and assigns it to a loop variable. The loop continues until every item has been processed.

string[] languages = { "C", "C++", "C#", "Java" };

foreach (string language in languages)
{
    Console.WriteLine(language);
}

Here, each string from the array is copied into the variable language one at a time. The loop body runs once for each element.

Syntax of foreach loop

foreach (type variable in collection)
{
    // code to execute for each item
}
PartMeaning
typeType of each item in the collection
variableTemporary variable for the current item
collectionObject being iterated
Loop bodyCode that runs for each item

The loop variable is available only inside the loop block. After one iteration finishes, it receives the next item from the collection.

foreach with List

foreach is commonly used with List<T>. Lists are dynamic collections, and foreach makes reading all items simple.

List<int> marks = new() { 80, 72, 91, 65 };

foreach (int mark in marks)
{
    Console.WriteLine(mark);
}

This loop does not care about index positions. It only cares about values. That makes the code shorter and easier to read when the index is not needed.

foreach with var Keyword

The var keyword is often used in foreach loops when the item type is obvious from the collection. The compiler still knows the real type at compile time.

foreach (var mark in marks)
{
    Console.WriteLine(mark);
}

This does not make the variable dynamic. If marks is a List<int>, then mark is inferred as int. Use explicit types when they improve readability, and use var when the type is clear from context.

foreach with String

A string can be iterated because it is a sequence of characters. Each iteration gives one char.

string name = "CSharp";

foreach (char ch in name)
{
    Console.WriteLine(ch);
}

This is useful for character counting, validation, parsing, and simple text processing. For advanced Unicode processing, characters may need deeper handling because one visible symbol can sometimes be made from multiple code units.

foreach with Dictionary

Dictionaries store key-value pairs. A foreach loop over a dictionary gives each pair as a KeyValuePair<TKey, TValue>.

Dictionary<string, int> scores = new()
{
    { "Asha", 90 },
    { "Ravi", 82 },
    { "Meera", 95 }
};

foreach (var pair in scores)
{
    Console.WriteLine($"{pair.Key}: {pair.Value}");
}

You can read both the key and value inside the loop. If you only need keys or only values, use scores.Keys or scores.Values.

break and continue in foreach

The break and continue statements work in foreach loops too. break stops the loop completely. continue skips the current item and moves to the next one.

foreach (int number in numbers)
{
    if (number < 0)
    {
        continue;
    }

    if (number == 100)
    {
        break;
    }

    Console.WriteLine(number);
}

This loop skips negative values and stops when it finds 100. This is useful when searching or filtering manually.

Can We Modify Collection in foreach?

In most cases, you should not add or remove items from a collection while iterating over it with foreach. Doing so usually causes an exception because the collection changes while the enumerator is reading it.

foreach (int number in numbers)
{
    if (number % 2 == 0)
    {
        numbers.Remove(number); // unsafe during foreach
    }
}

If you need to remove items, use a backward for loop, create a new filtered collection, or use methods such as RemoveAll when working with List<T>.

foreach vs for loop

Featureforeachfor
Best forReading each itemIndex-based control
Index accessNot directlyBuilt in
ReadabilityCleaner for traversalCleaner for counters and ranges
Removing by indexNot suitableSuitable when done carefully

Use foreach when you only need each item. Use for when you need the index, need to loop backward, or need exact numeric control.

How foreach Works Internally

A foreach loop works through enumeration. The collection provides an enumerator, and the enumerator moves from one item to the next. In many cases, this is based on IEnumerable or IEnumerable<T>, which are common interfaces in .NET collections.

You do not usually write enumerator code manually because foreach hides that detail. This is one reason the syntax is clean. The loop focuses on the current item instead of counters, indexes, and boundary checks.

IEnumerable<string> names = new List<string>
{
    "Asha",
    "Ravi",
    "Meera"
};

foreach (string name in names)
{
    Console.WriteLine(name);
}

Because the code depends on IEnumerable<T>, it can work with many collection types. This makes foreach flexible and readable in service code, API code, data processing, and business logic.

Getting Index in foreach loop

A foreach loop does not provide an index automatically. If you need the position of each item, you can maintain a separate counter, use LINQ with Select, or switch to a normal for loop. The best choice depends on how important the index is to the logic.

int index = 0;

foreach (string name in names)
{
    Console.WriteLine($"{index}: {name}");
    index++;
}

This is fine for simple display logic. But if the index controls array access, neighboring elements, reverse traversal, or removal, a for loop is usually clearer and safer.

Changing Items Inside foreach

The loop variable in a foreach loop should be treated as the current item, not as a way to replace the collection element. For value types such as int, assigning a new value to the loop variable does not update the original collection. In fact, C# does not allow assigning directly to the foreach iteration variable.

For reference types, the loop variable points to the current object. You cannot replace the item itself through the loop variable, but you can modify the object’s properties if the object is mutable.

foreach (User user in users)
{
    user.IsActive = true;
}

This updates the objects stored in the collection because each user variable refers to an object. Use this carefully because modifying many objects inside a loop can hide side effects if the code is not clearly named.

foreach with LINQ Results

foreach works naturally with LINQ query results because LINQ returns enumerable sequences. This is useful when you want to filter, project, or sort data before processing it.

var passedStudents = students
    .Where(student => student.Marks >= 40)
    .OrderBy(student => student.Name);

foreach (var student in passedStudents)
{
    Console.WriteLine(student.Name);
}

One important detail is deferred execution. Some LINQ queries do not run until you enumerate them. That means the query may execute when the foreach starts, not when the query variable is created. If this matters, convert the result to a list using ToList().

foreach and Null Collections

A foreach loop cannot iterate over a null collection. If the collection variable is null, the program throws a NullReferenceException. This is different from an empty collection, which simply runs zero iterations.

if (items != null)
{
    foreach (var item in items)
    {
        Console.WriteLine(item);
    }
}

In many designs, it is better to return an empty collection instead of null. An empty collection means there are no items. A null collection means the collection itself does not exist, which usually requires extra checks.

await foreach in C#

Modern C# also supports await foreach for asynchronous streams. This is used with IAsyncEnumerable<T>, where items may arrive over time from a network stream, database cursor, file pipeline, or background process.

await foreach (var message in GetMessagesAsync())
{
    Console.WriteLine(message);
}

This is different from a normal foreach because each next item may require asynchronous waiting. It is useful when data is produced gradually instead of already being available in memory.

Performance of foreach loop

For most C# applications, foreach performance is good enough and the readability benefit is worth it. The real performance cost usually comes from the work inside the loop, not from the loop keyword itself. Database calls, file operations, network requests, and heavy calculations inside a loop are much more important than choosing between for and foreach.

When performance is critical, measure the real code instead of guessing. A for loop may be faster in some index-based scenarios, but foreach is usually the better default for clear collection traversal. Readability should stay the first priority unless profiling shows a real bottleneck.

foreach with Custom Collections

Custom classes can also support foreach if they provide the correct enumeration pattern or implement IEnumerable<T>. This is useful when you build your own collection-like type and want it to behave naturally with C# loops.

For example, a custom playlist, sensor buffer, tree structure, or query result can expose items through enumeration. Once that is done, the calling code can use a normal foreach loop without knowing the internal storage details.

Common Mistakes with foreach loop

  • Trying to add or remove items from a collection during foreach.
  • Using foreach when the index is required for the logic.
  • Assuming foreach always means better performance.
  • Using unclear loop variable names such as x for important data.
  • Forgetting that dictionary iteration gives key-value pairs, not only values.

Best Practices for foreach loop in C#

Use foreach for clean collection traversal, choose meaningful loop variable names, avoid modifying the collection during enumeration, and switch to for when index control is required. The best loop is the one that makes the intention obvious with the least risk.


Continue learning C# in order
Follow the topic sequence with the previous and next lesson.