Created
May 10, 2022 02:40
-
-
Save shanecelis/acf9f198799a95f03d3d670ffc5c1e35 to your computer and use it in GitHub Desktop.
Demonstration of how to make an allocation-less struct IEnumerator.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** 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