I think it is very convenient the use of the ForEach statement when we want to loop through a list of items in C#. Simple and concise, easy to read but what is going on behind the scenes? Is the best option? This article will explore each option in detail.
Let’s start:
The old fashioned FOR loop
The for statement executes a statement or a block of statements while a specified Boolean expression evaluates to true. We just need to define a starting point and an increment pattern of the variable used as index.
public void ForLoop()
{
sum = 0;
for (int i = 0; i < elements.Count; i++)
{
sum += elements[i];
}
}
The compiler translates/refactors high-level language constructs to lower-level language constructs. This is important to understand as it could compromise the performance of your code. All these transformations happen before your code is compiled into IL, this step is called Lowering.
After lowering, the code would look like this:
public void ForLoop()
{
sum = 0;
int num = 0;
while (num < elements.Count)
{
sum += elements[num];
num++;
}
}
Ok, that’s interesting! The for statement has been replaced by while.
The cool kid, ForEach.
The foreach statement executes a statement or a block of statements for each element in an instance of the type that implements the System.Collections.IEnumerable or System.Collections.Generic.IEnumerable<T> interface.
public void ForEachLoop()
{
sum = 0;
foreach (var element in elements)
{
sum += element;
}
}
Without a doubt, this option is easier to read and understand as there is no need of indexes and increment patterns. After lowering, the code looks as follows:
public void ForEachLoop()
{
sum = 0;
List<int>.Enumerator enumerator = elements.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
int current = enumerator.Current;
sum += current;
}
}
finally
{
((IDisposable)enumerator).Dispose();
}
}
OK.. there’s some magic here, the code now includes try/finally in order to dispose the enumerator. However, the while statement remains the same, compared with the previous example.
Even fancier, List.ForEach
This option only applies to collections of type List allowing to execute an action for each element of the list, see more.
public void ForEachLambdaLoop()
{
sum = 0;
elements.ForEach(
x => sum += x
);
}
Looks nice to be honest, the method is expecting an Action that will be executed for each element in the collection. After lowering this code, this is how it looks like:
public void ForEachLambdaLoop()
{
sum = 0;
elements.ForEach(new Action<int>(<ForEachLambdaLoop>b__5_0));
}
[CompilerGenerated]
private void <ForEachLambdaLoop>b__5_0(int x)
{
sum += x;
}
Note that a new method to compute the arithmetic operation has been added and also decorated with CompilerGenerated.
How to compare all three options?
To get some metrics, I have created a sample project that you can download and use for your reference. In this case, I am using the BenchmarkDotNet package to analyze the code during its execution.
C:\temp\sharplab>dotnet add package BenchmarkDotNet
I have defined a basic class including three methods for each option discussed above. It is important to execute the code in Release mode for Benchmark to work.
C:\temp\sharplab>dotnet run -c release
The results
It is kind of shocking that a for statement executes in less than half of the time of the other two options. By looking at the code, I already knew that it would be the fastest option, but I had no idea how fast!

Summary
Two things to take away from this article:
- Understand the process of lowering in C#.
- Benchmark your code.
By the way, if someone says that your code is not efficient because you are initializing a List and later, adding each element by calling the .Add() function. They are wrong, it is the same thing after lowering.
Visit: https://sharplab.io/
private readonly List<int> elements;
private int sum = 0;
public Loop()
{
elements = new List<int>() {1,2,3,4,5,6,7,8,9,10};
}
private readonly List<int> elements;
private int sum = 0;
public Loop()
{
List<int> list = new List<int>();
list.Add(1);
list.Add(2);
list.Add(3);
list.Add(4);
list.Add(5);
list.Add(6);
list.Add(7);
list.Add(8);
list.Add(9);
list.Add(10);
elements = list;
}
Cheers!



