In this article, we will compare the for and foreach loops. Iterating through arrays and lists is fundamental when learning any programming language. C# offers different ways to achieve this, such as using while, for, and foreach statements.
But, what about the differences between different loops? Comparing for and foreach loops in C# and understanding their differences aids us in writing efficient code.
Without further delay, let’s start!
For Loop Statement in C#
As we all know, the for
loop is a general-purpose loop that we typically use to iterate through an indexable object while executing some statements and has this syntax:
for (initialization; condition; increment) { // statements }
If our set conditions remain true, we can initialize our counter and iterate through a collection.
How foreach Works in C#?
Besides the for loop
, we can use the foreach
loop to iterate through an IEnumerable
object:
foreach (element in iterable-item) { // statements }
Here, we can see that the foreach
statement does not need initialization and a counter, unlike the for
loop, making it easier to iterate through an object.
Comparing the Low-Level Implementation of for and foreach loops in C#
Assuming we have an array of elements that we want to output to the console using a for
loop:
public void PrintArray(int[] array) { for (int i = 0; i < array.Length; i++) { Console.WriteLine(array[i]); } }
Using SharpLab, let’s see the lowered version of the same snippet:
[NullableContext(1)] public void PrintArray(int[] array) { int num = 0; while (num < array.Length) { Console.WriteLine(array[num]); num++; } }
Next, we are going to repeat the same process using a foreach
loop:
public void PrintArray(int[] array) { foreach (var element in array) { Console.WriteLine(element); } }
Finally, let’s see the lowered version of the same function:
[NullableContext(1)] public void PrintArray(int[] array) { int num = 0; while (num < array.Length) { int value = array[num]; Console.WriteLine(value); num++; } }
We can see that the for
loop and foreach
loop are identical behind the scenes. However, the compiler output may differ based on logic or underlying data structures.
For example, let’s assume we are iterating through a List<int>
object and finding the sum of its elements:
public static long SumWithFor(List<int> data) { var sum = 0L; for(int i=0; i < data.Capacity; ++i) { sum += data[i]; } return sum; } public static long SumWithForEach(List<int> data) { var sum = 0L; foreach(var val in data) { sum += val; } return sum; }
Finally, let’s review it’s lowered version:
public static long SumWithFor(List<int> data) { long num = 0L; int num2 = 0; while (num2 < data.Capacity) { num += data[num2]; num2++; } return num; } public static long SumWithForEach(List<int> data) { long num = 0L; List<int>.Enumerator enumerator = data.GetEnumerator(); try { while (enumerator.MoveNext()) { int current = enumerator.Current; num += current; } return num; } finally { ((IDisposable)enumerator).Dispose(); } }
We can see that although the for
and foreach
loops utilize the while
loop, the latter in this case, uses the Enumerator()
object to iterate over the elements of the collection and is disposed of after the iteration process is complete.
foreach
on a List<T>
still generates code that initializes an enumerator; there were changes to the JIT compiler to optimize the performance of List<T>
enumeration.Compare For and Foreach Loops Performance in C#
Let’s test how the for
and foreach
loops compare when iterating through common data structures in C#. For these tests, we check how long it takes to find the sum of 10,000,000 integers:
| Method | Mean | Error | StdDev | Rank | Allocated | |----------------------- |----------:|----------:|----------:|-----:|----------:| | ArrayUsingForeach | 3.552 ms | 0.0130 ms | 0.0108 ms | 1 | 2 B | | ArrayUsingForLoop | 4.845 ms | 0.0148 ms | 0.0115 ms | 2 | 3 B | | | | | | | | | DictionaryUsingForeach | 13.702 ms | 0.1392 ms | 0.1234 ms | 1 | 6 B | | DictionaryUsingForLoop | 37.431 ms | 0.2907 ms | 0.2577 ms | 2 | 33 B | | | | | | | | | ListUsingForLoop | 5.429 ms | 0.0403 ms | 0.0357 ms | 1 | 3 B | | ListUsingForeach | 5.433 ms | 0.0259 ms | 0.0216 ms | 1 | 3 B |
Generally, the foreach
loop outshines its for
loop counterpart when iterating over arrays. When iterating over lists the performance is nearly identical. However, we benefit from having more control over the iteration logic when using for
loop.
foreach
will vary greatly on older .NET versions due to more recent optimizations.Besides that, iterating through dictionaries using for
loop requires us to define our logic to work with the <TKey, TValue>
pairs, which makes using this technique slower than the foreach
loop, where we have direct access to the elements.
Pros and Cons of Using for and Foreach Loop in C#
First, the awaitable foreach
loop allows iterating through asynchronous data streams for collections implementing the IAsyncEnumerable<T>
interface. With each iteration, we can suspend the process as we wait for the retrieval of the next element asynchronously, unlike using the for
loop.
Besides that, in situations where we need to perform some actions on every element of a type that implements the IEnumerable<T>
interface, the foreach
loop is the best bet. On the other hand, in cases where we need to access elements using their indices, do an action a fixed number of times, or need complex control over the flow, the for
loop is the best solution.
In terms of code readability, foreach
is cleaner, abstracting from collections and the iteration process. It also works if the collection type changes, unlike a for
loop, requiring us to refactor the code. For example, if we change an array into a list, we need to change the Length
property to Count
while initializing the loop.
Finally, the for
loop can come in handy in situations where we need to make changes to a collection, such as adding and removing elements, which is not possible when using the foreach
loop.
Conclusion
In this article, we learned how the for and foreach statements work in C#. We also learned the strengths and weaknesses of each statement when iterating through different data structures. Which one do you prefer? Let us know in the comments section below.