Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Demonstration of how to make an allocation-less struct IEnumerator.
/** Suppose you want to do a `foreach` on your collection without any
allocations. You know it's possible.
You've heard you need to use a struct IEnumerator, but how?
*/
using System.Collections;
using System.Collections.Generic;
/** You implement IEnumerable<T> in your collection class. */
public class MyEnumerable<T> : IEnumerable<T> {
T[] items;
public MyEnumerable(T[] items) => this.items = items;
/** You write your enumerator as a struct. */
public struct MyEnumerator : IEnumerator<T> {
int index;
MyEnumerable<T> myEnumerable;
public MyEnumerator(MyEnumerable<T> myEnumerable) {
this.myEnumerable = myEnumerable;
index = -1;
}
public bool MoveNext() => ++index < myEnumerable.items.Length;
public T Current => myEnumerable.items[index];
object IEnumerator.Current => Current;
public void Reset() => index = -1;
public void Dispose() {}
}
/** You implement the IEnumerable interface methods. But each of these will
still box the enumerator introducing an allocation. Yuck!
Note: These are not public. You can invoke them by casting
MyEnumerable<T> to IEnumerable<T>. */
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new MyEnumerator(this);
IEnumerator IEnumerable.GetEnumerator() => new MyEnumerator(this);
/** `foreach` only requires a `GetEnumerator()` method. It doesn't require
an IEnumerable<T> implementation at all. You return your struct--not as
an implementation of an interface like IEnumerator<T>
public IEnumerator<T> GetEnumerator() => new MyEnumerator(this); // NOT
but under its own struct name. Do this and you'll get an allocation-less
enumerator. */
public MyEnumerator GetEnumerator() => new MyEnumerator(this);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment