---
title: "Why is enumerating over List faster than IList?"
description: "If we have a `List<T>` and an `IList<T>`, enumerating over the `List<T>` is faster than enumerating over the `IList<T>`. Why is that?"
updated: 2025-06-02
tags: ["C#", ".NET", "Performance"]
canonical: "https://steven-giesel.com/blogPost/c624c170-c16c-4414-8506-2ae25ba54df7/why-is-enumerating-over-list-faster-than-ilist"
---

# Why is enumerating over List faster than IList?

If we have a `List<T>` and an `IList<T>`, enumerating over the `List<T>` is faster than enumerating over the `IList<T>`. Why is that?

## Benchmark Code

```csharp
[MemoryDiagnoser]
public class Benchmark
{
    private readonly List<int> numbersList = Enumerable.Range(0, 10_000).ToList();
    private readonly IList<int> numbersIList = Enumerable.Range(0, 10_000).ToList();

    [Benchmark(Baseline = true)]
    public int GetSumOfList()
    {
        var sum = 0;
        foreach (var number in numbersList) { sum += number; }
        return sum;
    }

    [Benchmark]
    public int GetSumOfIList()
    {
        var sum = 0;
        foreach (var number in numbersIList) { sum += number; }
        return sum;
    }
}
```

We have a result like this:

```no-class
| Method        | Mean      | Error     | StdDev    | Ratio | RatioSD | Allocated | Alloc Ratio |
|-------------- |----------:|----------:|----------:|------:|--------:|----------:|------------:|
| GetSumOfList  |  4.214 us | 0.0836 us | 0.0782 us |  1.00 |    0.03 |         - |          NA |
| GetSumOfIList | 19.508 us | 0.0459 us | 0.0384 us |  4.63 |    0.08 |      40 B |          NA |
```

## 40 bytes
Were are those 40 bytes coming from? When you call `List<T>.GetEnumerator()` (which will be done in every `foreach` loop) you get a struct named Enumerator. When calling `IList<T>.GetEnumerator()` you get a variable of type IEnumerator<T>, which contains a boxed version of said enumerator. That explains the 40 bytes of allocation and some of the performance difference.

	
## Virtual method calls
But there is a second dimension to this. When you call `IList<T>.GetEnumerator()`, it has to call a virtual method, which is slower than calling a non-virtual method. Basically, someone has to check which concrete type is really being used, and then call the correct method. This is not the case with `List<T>`, which is already the concrete type.