When it comes to creating loops in programming, the C# language offers plenty of options: while, do-while, for, foreach, and even the ForEach() method of the List<T> class. Among all of those, the C# foreach loop is arguably the most popular one, at least when it comes to iterating through the items of a collection. Using this language construct is quite intuitive, but do you really understand how it works?
Lucky for you, you’ve come to the right place, because this introductory guide is all about understanding how the C# foreach loop method works:
Let’s get started.
The foreach loop is one of the iteration statements that exist in C# language. The method enables you to iterate over a collection, executing the block of code you specify as many times as the number of items your collection contains. Also, for each execution of the loop, you access the current item from the collection.
You can use the foreach loop with arrays, lists (instances of the List<T> class), and any type that implements the Enuberable<T> interface.
This list isn’t exhaustive, but we’ll cover in more detail later in the post.
When creating applications, developers commonly work with collections of values. Here’s a non-exhaustive list of collections you might need to handle:
And when working with collections, an exceedingly common thing you might need to do is iterate through the collection and perform a given action to each one of its items. The foreach loop offers a clean, intuitive, and less error-prone way of performing that task.
The syntax of the foreach loop is quite easy and goes like this:
foreach (<type> <identifier> in <collection_identifier>)
And after that, you’d have your block of code to be executed. The <identifier> in syntax above is a variable you can use to access the current item from the collection, and of course it changes with each iteration of the loop. Let’s see a complete example:
var numbers = new List<int> {1, 2, 3, 4, 5};
foreach (var number in numbers)
{
Console.WriteLine($"The current number is {number} and its square is {number * number}");
}
As you can see, the example uses var (that is, type inference) instead of explicitly declaring the type, but the results are the same.
Let’s explore another scenario. What if you needed to stop executing my block of code before it’s finished, skipping to the next occurrence? For that, you can use the continue keyword:
List<string> theBeatles = ["John", "Ringo", "George", "Paul"];
foreach (var aBeatle in theBeatles)
{
Console.WriteLine($"This musician is called {aBeatle}");
if (aBeatle == "Ringo")
{
continue;
}
Console.WriteLine("This musician plays an instrument with strings.");
}
In the example above, we loop through a list containing the names of the four Beatles and print their names. If the current Beatle is Ringo, we skip to the next occurrence without printing the line that says that the current musician plays a string instrument. This is the result:
This musician is called John
This musician plays an instrument with strings.
This musician is called Ringo
This musician is called George
This musician plays an instrument with strings.
This musician is called Paul
This musician plays an instrument with strings.
What if you needed to stop the execution altogether, instead of just skipping to the next iteration? In such a case, you’d use the break statement:
List<string> theBeatles = ["John", "Ringo", "George", "Paul"];
foreach (var aBeatle in theBeatles)
{
if (aBeatle == "Ringo")
{
Console.WriteLine("We've found the drummer!");
break;
}
Console.WriteLine("No sign of the drummer yet :/");
}
Now, in this example, as soon as we find Ringo, we stop the execution with the final result being:
No sign of the drummer yet :/
We've found the drummer!
The foreach loop allows you to iterate through the items of a collection that can be enumerated. What exactly is a collection, though, and what does it mean to be enumerated?
Imagine an object that manages the items of a collection. You can ask two things of this object:
Objects like this are represented by the IEnumerator and IEnumerator<T> interface. With these interfaces, you get the Current property, which retrieves the current item. You also get the MoveNext() method, which returns true if there’s a next item and moves the Current pointer to the item or returns false otherwise.
Take a look at the following code:
List<string> theBeatles = ["John", "Ringo", "George", "Paul"];
IEnumerator<string> enumerator = theBeatles.GetEnumerator();
var currentBeatle = enumerator.Current;
Console.WriteLine(currentBeatle ?? "No item yet");
if (enumerator.MoveNext())
{
currentBeatle = enumerator.Current;
Console.WriteLine(currentBeatle);
}
if (enumerator.MoveNext())
{
currentBeatle = enumerator.Current;
Console.WriteLine(currentBeatle);
}
if (enumerator.MoveNext())
{
currentBeatle = enumerator.Current;
Console.WriteLine(currentBeatle);
}
if (enumerator.MoveNext())
{
currentBeatle = enumerator.Current;
Console.WriteLine(currentBeatle);
}
In the example above, we call GetEnumerator() on our list. With the enumerator, we fetch current, which is null, and that’s why “No item yet” gets printed. Then, we call MoveNext() four times in a row to move the “pointer” to the next one and print it.
Of course, that’s not a smart way to do it. We know this list is only four items long, but what if it was 4,000 or 4,000,000 items long? What if we had an unknown number of items?
A much saner approach would be to use a while loop:
List<string> theBeatles = ["John", "Ringo", "George", "Paul"];
IEnumerator<string> enumerator = theBeatles.GetEnumerator();
var currentBeatle = enumerator.Current;
Console.WriteLine(currentBeatle ?? "No item yet");
while (enumerator.MoveNext())
{
currentBeatle = enumerator.Current;
Console.WriteLine(currentBeatle);
}
That is, in a nutshell, what the foreach loop does. It is a form of syntactic suage over looping through the items of a collection using an enumerator.
Just to drive the point home, take a look at the following image:
The left side shows a normal code I wrote using the foreach loop, while the right panel shows the optimized version of it, optimized by the C# compiler. So, that’s what the foreach loop is under the hood: a while loop.
Now it’s time to finally understand with which types you can use a foreach loop. You’ll see a lot of people online saying that you can only use a foreach with types that implement the IEnumerable or IEnumerable<T> interface. After all, those interfaces expose a GetEnumerator() method that returns an enumerator and, as you’ve just seen, a foreach loop is nothing more than iterating through the elements of an enumerator.
In order to investigate that claim, let’s create our own foreacheable type! We’ll create a DateRange class:
public class DateRange : IEnumerable<DateTime>
{
public DateTime Start { get; }
public DateTime End { get; }
public DateRange(DateTime start, DateTime end)
{
Start = start;
End = end;
}
public bool Contains(DateTime date)
{
return date >= Start && date <= End;
}
public bool Overlaps(DateRange other)
{
return Start <= other.End && End >= other.Start;
}
public IEnumerator<DateTime> GetEnumerator()
{
for (var date = Start; date <= End; date = date.AddDays(1))
{
yield return date;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
A DateRange class fit for real-life suage would have included many more useful methods and properties, as well as parameter validation for the controller. For brevity’s sake, let’s keep our example as simple as possible. Based on the class above, we can easily write something like this:
var start = new DateTime(2024, 9, 1);
var end = new DateTime(2024, 9, 15);
var range = new DateRange(start, end);
foreach (var date in range)
{
Console.WriteLine(date.ToString("yyyy-MM-dd"));
}
And we get this result:
2024-09-01
2024-09-02
2024-09-03
2024-09-04
2024-09-05
2024-09-06
2024-09-07
2024-09-08
2024-09-09
2024-09-10
2024-09-11
2024-09-12
2024-09-13
2024-09-14
2024-09-15
Let’s now change the DateRange class to this:
public class DateRange
{
public DateTime Start { get; }
public DateTime End { get; }
public DateRange(DateTime start, DateTime end)
{
Start = start;
End = end;
}
public bool Contains(DateTime date)
{
return date >= Start && date <= End;
}
public bool Overlaps(DateRange other)
{
return Start <= other.End && End >= other.Start;
}
public IEnumerator<DateTime> GetEnumerator()
{
for (var date = Start; date <= End; date = date.AddDays(1))
{
yield return date;
}
}
}
Now, the code no longer implements the interface and no longer has the nongeneric version of GetEnumerator(). Running the code now, do we get an error?
Absolutely not. The results are the same.
Being able to use foreach with a custom type doesn’t require that the type implement IEnumerable or IEnumerable<T>, as along as the type has a GetEnumerator method that returns a valid enumerator.
Before wrapping up, let’s cover some best practices you should follow when working with a foreach in C#.
If you have a collection, and it can be null, you have to handle that before attempting to iterate the collection. Otherwise, you’ll get a null reference exception. The best and most concise way of doing that is using the null-coalescing operator:
foreach (var cust in customers ?? Enumerable.Empty<Customer>())
{
// your logic goes here
}
In this way, if the collection happens to be null, you get an empty one in its place, and the foreach simply doesn’t run.
One important piece of advice when using a foreach is don’t modify the collection while iterating it. By enumerating a collection, you’re using an enumerator that tracks the collection and its elements. Changing the collection renders the enumerator invalid and causes an exception with the following message:
System.InvalidOperationException: ‘Collection was modified; enumeration operation may not execute.’
What should you do if you need to change a collection while iterating it? In this case, the solution is to use a humble for loop. And most importantly, to loop the collection backwards, so the change in the indexes won’t affect the next items to be visited.
Sometimes people use a foreach loop for things that could easily be a LINQ instruction instead. Example:
IEnumerable<Employee> employees = GetEmployees();
var salaries = new List<decimal>();
foreach (var employee in employees)
{
if (employee.AdmissionDate.Year == 2024)
{
salaries.Add(employee.Salary);
}
}
The code above could be rewritten like this:
var salariesOfEmployeesAdmittedThisYear = employees
.Where(x => x.AdmissionDate.Year == 2024)
.Select(x => x.Salary)
.ToList();
When the type is obvious, prefer using var (type inference) rather than explicitly declaring the type. Type inference is more concise, clean, and makes the code less verbose.
With a proper understanding of syntax and best practices of the foreach construct in C#, developers have the ideal method of creating loops when iterating through a list of items in a collection. However, you still need to understand the other options for creating loops for different use cases to ensure your C# applications perform as intended.
Application monitoring solutions like Stackify Retrace help you ensure all your application deliver expected performance and a great user experience. To see how, start your Stackify Retrace trial today.
If you would like to be a guest contributor to the Stackify blog please reach out to [email protected]