Does foreach in Unity allocate memory?

In the company I work at, we have a strict rule to follow: don’t use foreach if you don’t have to. That means that instead iterating arrays (and lists) like this:

foreach (int n in myArray) 
{
}

we do it using a for loop:

for (int i = 0; i < myArray.Length; ++i)
{
    int n = myArray[i];
}

Back in the days of Unity 5, there was a bug in Unity’s compiler implementation, that caused foreach loop to allocate memory. Recently I was wondering: it has been over 6 years, 5 major Unity versions and a bunch of compiler improvements since then, so things has to be better now, right? Perhaps we can now get away from old superstitions and start using this sweet syntaxic sugar that has been around since forever at this point? So I made a little research to find that out, and learnt a few things along the way.

TL;DR

List<int> list = new List<int>();
foreach (int n in list)
{
    //Do something
}
IList<int> list = new List<int>();
foreach (int n in list) // allocates about 40B
{
    //Do something
}

The experiment

So to check memory allocations I did a simple thing: made a small script where I initialized a few common collections and iterated over them with foreach. I then ran this script through the Unity’s profiler and looked at the GC alloc column. Unity version used is 2020.3.10f. You can find the whole project used for this on my Github. Results looked like this:

Profiling results

So, no allocations? Woohoo! The bug is fixed, and we don’t have to worry about allocations anymore! But once the initial excitement cooled down and I thought about it a bit, I wondered: how come?

foreach and collections in .NET

The thing that made me wonder is the fact that foreach in .NET acts on types that implement the IEnumerable<T> interface 1. It exposes a single method: IEnumerator<T> GetEnumerator(), whilst IEnumerator<T> looks as following:

public interface IEnumerator<T> : IDisposable
{
    T Current { get; }

    bool MoveNext();

    void Reset();
}

So under the hood, any foreach statement expands more or less to this:

IEnumerator<T> enumerator = myCollection.GetEnumerator();
try
{
  while (enumerator.MoveNext()) 
  {
     T val = enumerator.Current;

     //foreach body goes here
  }
}
finally
{
   enumerator.Dispose();
}

My concern here was that since GetEnumerator() returns an instance of an interface, it must allocate memory. Even if the implementation returns an instance of a value-type, due to boxing it will still be put on the heap. However, it turns out that it doesn’t have to be that way. In MSDN article about foreach we can find this paragraph:

The foreach statement isn’t limited to those types. You can use it with an instance of any type that satisfies the following conditions:

  • A type has the public parameterless GetEnumerator method. Beginning with C# 9.0, the GetEnumerator method can be a type’s extension method.
  • The return type of the GetEnumerator method has the public Current property and the public parameterless MoveNext method whose return type is bool.

Note that the return type of GetEnumerator() doesn’t have to implement IEnumerator. That means that it’s possible to declare a GetEnumerator() that will return a struct and thus won’t allocate in foreach. In fact, that’s exactly what List<T> and other standard generic collections do. Here is the snippet from the List<T> class:

public List<T>.Enumerator GetEnumerator();

//List<T>.Enumerator
public struct Enumerator : IEnumerator<T>, IEnumerator, IDisposable { //snip }

So to summarize, the ability to use value-types for GetEnumerator() allows us not to allocate any memory while iterating with foreach. There is one thing to remember though.

Be careful with interfaces

While the compiler is able to find the right GetEnumerator() method in List or HashSet, it doesn’t have much choice when given an object of type IList or ICollection. When it sees an interface, its only option is to use GetEnumerator() from the IEnumerable interface, which all of these interfaces implement. This method returns an object of type IEnumerator which is reference-type, therefore it will be allocated on heap regardless of the actual implementation of the interface. Let’s consider an example. Let’s say we create an instance of List<int> and pass it to another method to calculate the average. But the Average method takes IReadOnlyList<int> as a parameter, to provide some degree of abstraction.

List<int> list = new List<int> { 4, 24, 13, 42 };
float average = Average(list);

private float Average (IReadOnlyList<int> numbers)
{
    float average = 0;

    //Here IEnumerator<int> IEnumerable<int>.GetEnumerator() will be used
    //allocating memory on the heap
    foreach (int n in numbers) 
    {
        average += n;
    }

    return average / numbers.Count;
}

Because of this if we really want to use foreach on collections, we need to first make sure that the one we work with is actually of a concrete type, not an interface. And if this seems doable, it’s impossible to make sure that another person working on this piece of code tomorrow won’t change the type to an interface and you will not get the additional GC allocation. Thus, while it’s sometimes tempting to save a few seconds of typing and write a foreach, I’d still recommend sticking to for wherever possible. This is just more robust and reliable solution, which also is more performant in terms of CPU.


  1. In this article I focus on generic collections like List<T> and Dictionary<TKey, TValue>. Their non-generic versions work in similar way. ↩︎