ZetCode

C# foreach

last modified October 18, 2026

In this article we show how to loop over data in C# with the foreach statement and the ForEach method.

foreach (var val in vals)
{
    ...
}

The foreach statement executes a statement or a block of statements for each element of a collection. The collection can be anything that has a GetEnumerator method: it implements IEnumerable or IEnumerable<T>, or follows the same pattern, like arrays and Span<T>. The iteration variable is read-only.

public void ForEach(Action<T> action);

The List<T>.ForEach method performs the specified action on each element of the list. A similar method exists for arrays: Array.ForEach. There is no ForEach method for a general IEnumerable<T>.

The foreach statement is the preferred way to loop, as it supports break, continue, return, and await, which the ForEach methods do not.

C# foreach array

In the following example, we go over the elements of an array using the foreach statement.

Program.cs
int[] vals = [1, 2, 3, 4, 5];

foreach (var val in vals)
{
    Console.WriteLine(val);
}

// the iteration variable is read-only
foreach (var val in vals)
{
    // val = 10;        // error CS1656: cannot assign to 'val' because it is a 'foreach iteration variable'
    Console.Write($"{val * 2} ");
}

Console.WriteLine();

The example prints all elements of an array of integers, and then the doubled values.

foreach (var val in vals)
{
    // val = 10;

The iteration variable cannot be assigned, a compile-time error (CS1656). To change the elements of an array, use a for loop with an index. (If the element is a reference type or a class instance, its properties can be changed, as shown below.)

$ dotnet run
1
2
3
4
5
2 4 6 8 10

C# foreach multidimensional array

The next example loops over a two-dimensional array and a jagged array.

Program.cs
int[,] vals =
{
    {1, 2, 3, 4, 5},
    {6, 7, 8, 9, 10},
    {11, 12, 13, 14, 15}
};

foreach (var e in vals)
{
    Console.Write($"{e} ");
}

Console.WriteLine();

Console.WriteLine("------------------------------------");

for (int i = 0; i < vals.GetLength(0); i++)
{
    for (int j = 0; j < vals.GetLength(1); j++)
    {
        Console.Write($"{vals[i, j],3}");
    }

    Console.WriteLine();
}

Console.WriteLine("------------------------------------");

// a jagged array is an array of arrays
int[][] jagged = [[1, 2], [3, 4, 5], [6]];

foreach (int[] row in jagged)
{
    foreach (int e in row)
    {
        Console.Write($"{e} ");
    }

    Console.WriteLine();
}

We have a two-dimensional array of integers. We loop over the values in several ways.

foreach (var e in vals)
{
    Console.Write($"{e} ");
}

A foreach over a rectangular array visits all elements in row-major order, row after row, as one flat sequence.

for (int i = 0; i < vals.GetLength(0); i++)
{
    for (int j = 0; j < vals.GetLength(1); j++)
    {
        Console.Write($"{vals[i, j],3}");
    }

    Console.WriteLine();
}

To keep the row structure, we use two nested for loops; GetLength(0) is the number of rows and GetLength(1) the number of columns. The alignment component ,3 right-aligns each number in a field three characters wide.

int[][] jagged = [[1, 2], [3, 4, 5], [6]];

foreach (int[] row in jagged)
{
    foreach (int e in row)

A jagged array is an array of arrays; its rows may have different lengths. Two nested foreach loops traverse it naturally.

$ dotnet run
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
------------------------------------
  1  2  3  4  5
  6  7  8  9 10
 11 12 13 14 15
------------------------------------
1 2
3 4 5
6

C# foreach List

In the following example, we loop over a list with the foreach statement.

Program.cs
List<string> words = ["tea", "falcon", "book", "sky"];

foreach (var word in words)
{
    Console.WriteLine(word);
}

Console.WriteLine("--");

// with an index
foreach (var (word, index) in words.Select((w, i) => (w, i)))
{
    Console.WriteLine($"{index}: {word}");
}

Console.WriteLine("--");

// break and continue
foreach (var word in words)
{
    if (word == "book")
    {
        continue;
    }

    if (word == "sky")
    {
        break;
    }

    Console.WriteLine(word);
}

// reversed and ordered
foreach (var word in words.AsEnumerable().Reverse())
{
    Console.Write($"{word} ");
}

Console.WriteLine();

foreach (var word in words.OrderBy(w => w.Length).ThenBy(w => w))
{
    Console.Write($"{word} ");
}

Console.WriteLine();

We print the elements of a list of strings in several ways.

foreach (var (word, index) in words.Select((w, i) => (w, i)))
{
    Console.WriteLine($"{index}: {word}");
}

The foreach statement does not provide an index. When we need one, the LINQ Select overload with an index builds pairs, which are deconstructed in the loop header. Alternatively, we use a for loop.

if (word == "book")
{
    continue;
}

if (word == "sky")
{
    break;
}

The continue statement skips to the next element, and break leaves the loop. Here, the book is skipped and the loop ends at the sky.

foreach (var word in words.AsEnumerable().Reverse())
foreach (var word in words.OrderBy(w => w.Length).ThenBy(w => w))

LINQ operators give another order to the loop without changing the list. The AsEnumerable call makes sure that the LINQ Reverse is used and not the in-place span method of newer C# versions.

$ dotnet run
tea
falcon
book
sky
--
0: tea
1: falcon
2: book
3: sky
--
tea
falcon
sky book falcon tea
sky tea book falcon

C# foreach list of lists

In the next example, we loop over a list of lists.

Program.cs
List<List<string>> words =
[
    ["tea", "falcon", "book", "sky"],
    ["cup", "crown", "borrow", "moore"],
    ["arm", "nice", "frost", "sea"]
];

foreach (var nested in words)
{
    foreach (var word in nested)
    {
        Console.Write($"{word} ");
    }

    Console.WriteLine();
}

// flatten with SelectMany
foreach (var word in words.SelectMany(w => w).Where(w => w.Length > 5))
{
    Console.WriteLine(word);
}

To iterate over a list of lists, we use two foreach loops. When we do not need the row structure, LINQ's SelectMany flattens the nested collection into a single sequence, and a single loop is enough.

$ dotnet run
tea falcon book sky
cup crown borrow moore
arm nice frost sea
falcon
borrow

C# foreach Dictionary

The following example loops over the elements of a dictionary with the foreach statement.

Program.cs
var domains = new Dictionary<string, string>
{
    {"sk", "Slovakia"},
    {"ru", "Russia"},
    {"de", "Germany"},
    {"no", "Norway"}
};

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

Console.WriteLine("-----------------------");

foreach (var (key, value) in domains)
{
    Console.WriteLine($"{key} - {value}");
}

Console.WriteLine("-----------------------");

foreach (var key in domains.Keys.Order())
{
    Console.WriteLine($"{key} - {domains[key]}");
}

Console.WriteLine("-----------------------");

foreach (var value in domains.Values.OrderByDescending(v => v))
{
    Console.Write($"{value} ");
}

Console.WriteLine();

We have a dictionary of domains. The elements of a dictionary are KeyValuePair<TKey, TValue> structures.

foreach (var (key, value) in domains)
{
    Console.WriteLine($"{key} - {value}");
}

A KeyValuePair can be deconstructed into the key and the value directly in the loop header, which makes the body shorter. The earlier form with pair.Key and pair.Value is equivalent.

foreach (var key in domains.Keys.Order())
{
    Console.WriteLine($"{key} - {domains[key]}");
}

A Dictionary does not guarantee any order of its elements; in practice, it is the order of insertion when nothing has been removed. If an order matters, we sort the keys or the values, here with the Order method (.NET 7). A SortedDictionary keeps the keys sorted all the time.

$ dotnet run
sk - Slovakia
ru - Russia
de - Germany
no - Norway
-----------------------
sk - Slovakia
ru - Russia
de - Germany
no - Norway
-----------------------
de - Germany
no - Norway
ru - Russia
sk - Slovakia
-----------------------
Slovakia Russia Norway Germany

C# foreach over other sequences

We can use foreach with many types: strings, the results of LINQ queries, ranges, spans, file system enumerations, and so on.

Program.cs
// foreach over a string, a range, and the result of a LINQ query
foreach (char c in "falcon")
{
    Console.Write($"{char.ToUpper(c)} ");
}

Console.WriteLine();

foreach (int i in Enumerable.Range(1, 5))
{
    Console.Write($"{i * i} ");
}

Console.WriteLine();

foreach (var line in "one\ntwo\nthree".Split('\n'))
{
    Console.WriteLine(line.Length);
}

// over a span: no allocations
ReadOnlySpan<int> span = [10, 20, 30];
foreach (var e in span)
{
    Console.Write($"{e} ");
}

Console.WriteLine();

The example loops over a string, a numeric range, an array of lines, and a span.

ReadOnlySpan<int> span = [10, 20, 30];
foreach (var e in span)
{
    Console.Write($"{e} ");
}

A Span<T> is a ref struct with a GetEnumerator method; the loop does not allocate any memory. This is the pattern-based side of foreach: no interface is needed.

$ dotnet run
F A L C O N
1 4 9 16 25
3
3
5
10 20 30

C# foreach and modifying the collection

We must not add or remove elements of a collection while we enumerate it with foreach. The enumerator detects the change and throws an InvalidOperationException on the next step.

Program.cs
List<int> numbers = [1, 2, 3, 4, 5, 6];

try
{
    foreach (var n in numbers)
    {
        if (n % 2 == 0)
        {
            numbers.Remove(n);       // changes the collection during the enumeration
        }
    }
}
catch (InvalidOperationException e)
{
    Console.WriteLine(e.Message);
}

// remove with RemoveAll, or enumerate a copy
numbers.RemoveAll(n => n % 2 == 0);
Console.WriteLine(string.Join(", ", numbers));

List<int> numbers2 = [1, 2, 3, 4, 5, 6];
foreach (var n in numbers2.ToList())
{
    if (n % 2 == 0)
    {
        numbers2.Remove(n);
    }
}

Console.WriteLine(string.Join(", ", numbers2));

// a for loop going backwards also works
List<int> numbers3 = [1, 2, 3, 4, 5, 6];
for (int i = numbers3.Count - 1; i >= 0; i--)
{
    if (numbers3[i] % 2 == 0)
    {
        numbers3.RemoveAt(i);
    }
}

Console.WriteLine(string.Join(", ", numbers3));

// modifying the properties of the elements is fine
var people = new List<Person> { new("John", 30), new("Jane", 25) };
foreach (var p in people)
{
    p.Age++;
}

Console.WriteLine(string.Join(", ", people));

class Person(string name, int age)
{
    public int Age { get; set; } = age;
    public override string ToString() => $"{name} {Age}";
}

The example tries to remove the even numbers from a list in the loop, and then shows three ways that work.

numbers.RemoveAll(n => n % 2 == 0);

RemoveAll is the shortest way for a list. Another way is to enumerate a copy of the list (numbers.ToList()) while changing the original, or a for loop that goes backwards, so that the removal does not shift the indexes that are still to be visited.

foreach (var p in people)
{
    p.Age++;
}

Changing the properties of the elements is fine, as long as the elements are reference types. For a collection of structures, the loop variable is a copy, and the change is lost (and the compiler rejects changing its field).

$ dotnet run
Collection was modified; enumeration operation may not execute.
1, 3, 5
1, 3, 5
1, 3, 5
John 31, Jane 26

C# foreach with custom collections

Our own types can be used in foreach by implementing IEnumerable<T>, or just by having a suitable GetEnumerator method. The easiest way to write the enumerator is an iterator method with yield return.

Program.cs
// foreach works with any type that has a GetEnumerator method
var countdown = new Countdown(5);

foreach (var n in countdown)
{
    Console.Write($"{n} ");
}

Console.WriteLine();

foreach (var w in new Words("an old falcon in the sky"))
{
    Console.WriteLine(w);
}

// an iterator method with yield return
foreach (var n in Fibonacci().Take(10))
{
    Console.Write($"{n} ");
}

Console.WriteLine();

IEnumerable<long> Fibonacci()
{
    long a = 0, b = 1;

    while (true)
    {
        yield return a;
        (a, b) = (b, a + b);
    }
}

class Countdown(int from) : IEnumerable<int>
{
    public IEnumerator<int> GetEnumerator()
    {
        for (int i = from; i > 0; i--)
        {
            yield return i;
        }
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
}

// duck typing: only a GetEnumerator method with Current and MoveNext is needed
class Words(string text)
{
    public IEnumerator<string> GetEnumerator() => text.Split(' ').AsEnumerable().GetEnumerator();
}

The example defines a countdown collection, a class that only has a GetEnumerator method, and an infinite sequence of Fibonacci numbers.

IEnumerable<long> Fibonacci()
{
    long a = 0, b = 1;

    while (true)
    {
        yield return a;
        (a, b) = (b, a + b);
    }
}

An iterator method runs only as far as the consumer asks. The sequence is infinite, but Take(10) stops after ten elements. Nothing is computed ahead of time and nothing is stored.

$ dotnet run
5 4 3 2 1
an
old
falcon
in
the
sky
0 1 1 2 3 5 8 13 21 34

C# await foreach

The await foreach statement (C# 8) consumes an asynchronous stream, an IAsyncEnumerable<T>. Elements arrive over time, for example from a network or a database, and the loop waits for each of them without blocking a thread.

Program.cs
// await foreach consumes an asynchronous stream
await foreach (var n in ReadAsync())
{
    Console.WriteLine($"received {n}");
}

// asynchronous streams can be cancelled
using var cts = new CancellationTokenSource(350);

try
{
    await foreach (var n in ReadAsync(cts.Token))
    {
        Console.WriteLine($"again {n}");
    }
}
catch (OperationCanceledException)
{
    Console.WriteLine("canceled");
}

async IAsyncEnumerable<int> ReadAsync([System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken token = default)
{
    for (int i = 1; i <= 3; i++)
    {
        await Task.Delay(200, token);
        yield return i;
    }
}

The program reads three numbers that arrive with a delay, and then a second time with a timeout.

async IAsyncEnumerable<int> ReadAsync([EnumeratorCancellation] CancellationToken token = default)

An asynchronous iterator combines async, await, and yield return. The attribute EnumeratorCancellation connects the token passed through WithCancellation on the consumer side with the parameter of the method. In the example, the token comes from the argument, and the cancellation ends the loop with an OperationCanceledException after the first element.

$ dotnet run
received 1
received 2
received 3
again 1
canceled

C# ForEach method

The ForEach methods of arrays and lists run a delegate for each element. They are fine for short statements, but they are less flexible than the statement.

Program.cs
int[] vals = [1, 2, 3, 4, 5];

Array.ForEach(vals, e => Console.Write($"{e} "));
Console.WriteLine();

List<string> words = ["tea", "falcon", "book", "sky"];
words.ForEach(e => Console.Write($"{e} "));
Console.WriteLine();

// a method group works too
words.ForEach(Console.WriteLine);

var domains = new Dictionary<string, string>
{
    {"sk", "Slovakia"},
    {"ru", "Russia"}
};

domains.ToList().ForEach(pair => Console.WriteLine($"{pair.Key} - {pair.Value}"));

// there is no ForEach on IEnumerable<T>; we can write our own, but a foreach statement is clearer
IEnumerable<int> seq = Enumerable.Range(1, 3);
// seq.ForEach(...)       // error CS1061: IEnumerable<int> does not contain a definition for 'ForEach'
foreach (var s in seq) Console.Write($"{s} ");
Console.WriteLine();

// a ForEach lambda cannot break or return from the outer method
int sum = 0;
vals.ToList().ForEach(v => { if (v > 3) return; sum += v; });      // return only leaves the lambda
Console.WriteLine(sum);

The program uses Array.ForEach and List<T>.ForEach.

words.ForEach(Console.WriteLine);

A method group can be passed instead of a lambda.

domains.ToList().ForEach(pair => Console.WriteLine($"{pair.Key} - {pair.Value}"));

For a dictionary, which has no ForEach, we first copy the elements to a list with LINQ's ToList. The extra copy makes this form worse than a plain foreach.

vals.ToList().ForEach(v => { if (v > 3) return; sum += v; });

The limits of the method show here. A return inside the lambda leaves only the lambda, so it works like continue; there is no way to break out of the loop. We also cannot use await in a useful way. And the lambda can capture variables of the outer method, which allocates a closure. For these reasons, most style guides recommend the foreach statement.

$ dotnet run
1 2 3 4 5
tea falcon book sky
tea
falcon
book
sky
sk - Slovakia
ru - Russia
1 2 3
6

C# Parallel.ForEach

For CPU-bound work on large collections, Parallel.ForEach spreads the iterations over several threads. The order of the iterations is not defined. For asynchronous work, there is Parallel.ForEachAsync with a limit on the concurrency.

Program.cs
using System.Diagnostics;

var items = Enumerable.Range(1, 8).ToList();

// Parallel.ForEach spreads the iterations over the thread pool: the order is not defined
var bag = new System.Collections.Concurrent.ConcurrentBag<int>();
Parallel.ForEach(items, i => bag.Add(i * i));
Console.WriteLine(string.Join(", ", bag.Order()));

// Parallel.ForEachAsync for asynchronous work with a limit
var sw = Stopwatch.StartNew();
await Parallel.ForEachAsync(items, new ParallelOptions { MaxDegreeOfParallelism = 4 }, async (i, ct) =>
{
    await Task.Delay(100, ct);
});
Console.WriteLine($"8 delays of 100 ms with 4 workers: about {sw.ElapsedMilliseconds / 100 * 100} ms");

// do not await in a List.ForEach lambda: the lambda becomes async void
// items.ForEach(async i => await Task.Delay(100));      // fire-and-forget, exceptions crash the process

// a plain foreach with await runs one at a time
sw.Restart();
foreach (var i in items.Take(3))
{
    await Task.Delay(100);
}
Console.WriteLine($"3 sequential delays: about {sw.ElapsedMilliseconds / 100 * 100} ms");

The program squares numbers in parallel and runs asynchronous delays with four workers.

var bag = new System.Collections.Concurrent.ConcurrentBag<int>();
Parallel.ForEach(items, i => bag.Add(i * i));

The body runs on several threads at once, so any shared state needs a thread-safe collection, here a ConcurrentBag; adding to a plain List from the loop would be a data race. We sort the result, since the bag has no order.

// items.ForEach(async i => await Task.Delay(100));

An async lambda passed to List.ForEach becomes an async void method: the call does not wait for the work, and an exception in it can crash the process. Use Parallel.ForEachAsync for concurrent work or a plain foreach with await for sequential work, as the last part of the example shows.

$ dotnet run
1, 4, 9, 16, 25, 36, 49, 64
8 delays of 100 ms with 4 workers: about 200 ms
3 sequential delays: about 300 ms

Source

The foreach statement

In this article we have covered the foreach statement and the ForEach method in C#.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all C# tutorials.