---
title: "IEnumerable vs IEnumerator in C#: One is 2x Faster - LinkedIn Edition"
description: "During my recent browsing on **LinkedIn** I saw that question: **IEnumerable vs IEnumerator in C#: One is 2x Faster – Which One?**\n\nNaturally, I was very suspicious. So let's find out what is going on here."
updated: 2025-02-17
tags: ["C#", ".NET", "Performance", "Linkedin"]
canonical: "https://steven-giesel.com/blogPost/b705be3e-8d80-4de1-aefb-29c9262c49b7/ienumerable-vs-ienumerator-in-c-one-is-2x-faster-linkedin-edition"
---

# IEnumerable vs IEnumerator in C#: One is 2x Faster - LinkedIn Edition

During my recent browsing on **LinkedIn** I saw that question: **IEnumerable vs IEnumerator in C#: One is 2x Faster – Which One?**

Naturally, I was very suspicious. So let's find out what is going on here.

## The code

Let's start with the code in question:
```csharp
public class EnumerableVsEnuemrator
{
    private List<decimal> _productPrices = [19.99m, 34.50m, 50m, 12.75m, 99.99m, 14.30m, 5.99m, 20.15m];

    [Benchmark]
    public void TestIEnumerable()
    {
        IEnumerable<decimal> priceCollection = _productPrices;
        foreach (var price in priceCollection)
        {
            Debug.WriteLine(price);
        }
    }
    
    [Benchmark]
    public void TestIEnumerator()
    {
        IEnumerator<decimal> priceEnumerator = _productPrices.GetEnumerator();
        while (priceEnumerator.MoveNext())
        {
            Debug.WriteLine(priceEnumerator.Current);
        }
    }
    
    public static void Main() => BenchmarkRunner.Run<EnumerableVsEnuemrator>();
}
```

So the same list will be taken for the enumeration, but once with `IEnumerable` and once with `IEnumerator`. So what is the difference between those two?

### IEnumerable vs IEnumerator

In very simple terms: `IEnumerable` tells you that something is enumerable. Like, you have a basket of apples. But how to go from one apple to the next? That's where `IEnumerator` comes in. The enumerator describes how to go from one apple to the next (or when you are done).

Spoiler-alert: `IEnumerable` is defined as (simplified):
```csharp
public interface IEnumerable<out T>
{
  IEnumerator<T> GetEnumerator();
}
```

So, `IEnumerable` is just a wrapper around `IEnumerator`. So, why then the 2x times difference in speed?

## The problem with the benchmark

The whole code has **three** major problems:
 * **Problem 1**: The `Debug.WriteLine` is a poor choice of "Act". Mainly because it is backed by the `ConditionalAttribute`.
 * **Problem 2**: The sample size of 8 elements is way too low. Like way way way too low.
 * **Problem 3**: No return value. The act (the `Debug.WriteLine`) is not returning anything. So the compiler can optimize the whole loop away. 

So all in all, those two pieces of code are not comparable. And by the way, on my machine, the results look like this:
```no-class
| Method          | Mean     | Error    | StdDev   |
|---------------- |---------:|---------:|---------:|
| TestIEnumerable | 16.90 ns | 0.044 ns | 0.034 ns |
| TestIEnumerator | 11.15 ns | 0.095 ns | 0.084 ns |
```

It is always important to measure for your specific problem (and that also includes platform and so on). Anyway, let's go through the problems one by one.

### Problem 1: Debug.WriteLine
If we have a look at the [`Debug.WriteLine`](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.debug.writeline?view=net-9.0) method, we see that it is backed by the `ConditionalAttribute`. 

```csharp
public static class Debug
{
    [Conditional("DEBUG")]
    public static void WriteLine(string message, params object?[] args) { ... }
}
```

The important bid here is two-folded: 
If you use the `ConditionalAttribute` on a method with "DEBUG", the compiler will remove the content of your method if the `DEBUG` symbol is not defined. So, in a release build, the whole `Debug.WriteLine` body will be removed. Therefore you have an empty method. Even if that wouldn't be the case, `Debug.WriteLine` is a method with side-effects (like writing to the console). This is not a good choice for a benchmark as it has a big influence on the results.

### Problem 2: Sample size
The sample size is way too low. Only having a few elements will not be sufficient to filter out noise! To make that somewhat useful, take at least 10000 elements.

### Problem 3: No return value
The act (the `Debug.WriteLine`) is not returning anything and paired with **Problem 1** the compiler can optimize the whole loop away. So, the whole benchmark is not doing anything and therefore those two results are not comparable!

## Lowered Code
And yes, **Problem 3** is the reality here:
```csharp
public void TestIEnumerable()
{
    IEnumerator<decimal> enumerator = ((IEnumerable<decimal>)_productPrices).GetEnumerator();
    try
    {
        while (enumerator.MoveNext())
        {
            decimal current = enumerator.Current;
        }
    }
    finally
    {
        if (enumerator != null)
        {
            enumerator.Dispose();
        }
    }
}

public void TestIEnumerator()
{
    IEnumerator<decimal> enumerator = _productPrices.GetEnumerator();
    while (enumerator.MoveNext())
    {
    }
}
```

<sup>Source on [Sharlab.io](https://sharplab.io/#v2:D4ehAIGUFNvALALogDgZwFxgIYGtsB0AtmiAHbQDuAtAMYD2Za9ANtNYtESi9p+ADN6AJ3BERcAJZkhwon0mMAsAChVAAQAM4dQEYCAEUnYA5mXppEk2mgDcG7XoAs9tSvUBmHQCZwAUTIAVyJoYWwAIzYANTQAwK4wxBFVAG9VcAzwFGFJADc+OAAZSUsAHgATaFpJeRYAPnAAfWz6csDaRAAFHNpoNHAAXnAAbV0ATgIxsaIAGnAPJwIAVk1Z8BW13W8CAHYltanJ6bndRY9VuaWjte9NAl19gF1XdMzPHSdwABU+xD0PAAUAEpXhk0ipMpCdLoPBUqjVsPUsj1oABhVhsDqKMiDJotNodbrWPquKGZWTQbC0eDgAH5UTZYngaTI4noliYqyMEEQsngcF8qEGaDhQImAgAdRynGKFABjN6QNJfIAvqDwGreRl1e91J8fpYAJJxEKJETA9UCvnGoKmvgiOHVWoNBXQE2he2iIbNYStdpdFFoAgAcWgiHdZuEwOVZMo8EkbFprojnoIAFl6LloAA5aAAD0QwJ5gqtgoywtF4qlkhl0mg8pRKaSwgIqMCwmE0DIiCV6shmv76p13m8lvVmpVQA===)</sup>

The whole body is getting removed! So, the whole benchmark is not doing anything and therefore those two results are not comparable!

## Let's fix it
Let's address all the issues and make a proper benchmark out of it.

```csharp
public class EnumerableVsEnuemrator
{
    private List<decimal> _productPrices = Enumerable.Range(0, 10_000)
        .Select(i => (decimal)i)
        .ToList();

    [Benchmark]
    public decimal TestIEnumerable()
    {
        IEnumerable<decimal> priceCollection = _productPrices;
        var sum = 0m;
        foreach (var price in priceCollection)
        {
            sum += price;
        }
        
        return sum;
    }
    
    [Benchmark]
    public decimal TestIEnumerator()
    {
        IEnumerator<decimal> priceEnumerator = _productPrices.GetEnumerator();
        var sum = 0m;
        while (priceEnumerator.MoveNext())
        {
            sum += priceEnumerator.Current;
        }
        
        return sum;
    }
    
    public static void Main() => BenchmarkRunner.Run<EnumerableVsEnuemrator>();
}
```

With the following result:
```no-class
| Method          | Mean     | Error    | StdDev   |
|---------------- |---------:|---------:|---------:|
| TestIEnumerable | 58.12 us | 0.647 us | 0.605 us |
| TestIEnumerator | 51.97 us | 0.308 us | 0.257 us |
```

Not so flashy after all.It is a bit slower due to the overhead of the `foreach` loop (and virtual method calls). 
Therefore: Always measure for your specific problem and don't trust everything blindly you read on the internet.