Well actually, since most enumerators in the built in collections are ‘structs’, if you access them directly you can avoid allocating an object on the heap to later be garbage collected. Instead having a temporary struct on the stack… that is unless you force it on the heap.
var lst = new List<object>();
//fill lst
List<object>.Enumerator e = lst.GetEnumerator();
while(e.MoveNext())
{
var obj = e.Current;
}
As you can see ListEnumerator is a struct:
Using this technique of the ‘while(e.MoveNext())’ is very common in the unity community due to the performance hit GC calls take on games (the old mono runtime unity uses has a very naive non-generational garbage collection implementation). Despite if many people who do it, don’t know WHY they’re actually doing it.
So by using the ‘foreach’ you are forcing the enumerator onto the heap. This is because the struct implements IEnumerator, and the foreach treats it as the interface type rather than directly as the concrete type. This is because the underlying IL doesn’t actually have the concept of a ‘foreach’ loop and it actually gets unraveled as the while(e.MoveNext()), but the ‘e’ is typed IEnumerator instead. The issue being that when you cast a struct as its interface, it gets boxed, and placed on the heap.
This boxing is where the garbage is coming from.
Hence why we use that same boilerplate to avoid the gc.
Now there’s a way this could be fixed, and that’d be if the compiler didn’t coerce the struct Enumerator into an IEnumerator, but instead typed it as its concrete type. I’m not sure if this is actually fixed in Unity 5.5, haven’t looked to confirm (don’t have anything to test that with on hand right this moment). But it is a possibility.
I hope to check when I get back to my house and can install Unity 5.5 onto a virtual and take a look at the IL generated.
Note though, if it were fixed… it’d only count if your source collection implements it Enumerator as a struct, AND collection is referenced as its concrete type as well. If you use a List, but store it in a variable of type IList, the boxing will still occur as the compiler wouldn’t know which type it is specifically and would have to rely on the generic IList interface which only returns IEnumerator. The complexity of sorting out all this based on the type in question is why the compiler just coerces to IEnumerator in all cases (accept array), because it’s just faster/easier. And the old compiler used by Unity (pre 5.5) is rather… proof-of-concept… from the early days of mono. So they cut a lot of corners (mono community, not unity specifically).