In this lesson, you will learn how to move beyond simple loops by implementing the IEnumerator<T> interface to gain granular control over how your collections are traversed.
The Enumerator Pattern Defined
At its core, the Enumerator Pattern is the "engine" behind the foreach loop. While a collection is a container of items, an Enumerator is a state machine that knows exactly where you are within that container. By manually implementing this pattern, you aren't just storing data; you are defining the exact sequence in which that data is consumed.
Designing the Traversal
When you implement IEnumerator<T>, you are responsible for three main moving parts:
Current: A property that returns the element at the current cursor position.MoveNext(): A method that advances the cursor to the next item, returningfalseif the end of the collection is reached.Reset(): A method to return the cursor to its initial state.
Consider a custom collection that holds data in an unusual structure, like a tree or a complex array. Instead of exposing your internal memory layout, you build an enumerator that hides that complexity:
public class CustomEnumerator<T> : IEnumerator<T>
{
private readonly T[] _items;
private int _index = -1;
public CustomEnumerator(T[] items) => _items = items;
public T Current => _items[_index];
object IEnumerator.Current => Current;
public bool MoveNext()
{
// Custom logic: skip every second item?
// Only return non-null items? You control it!
_index++;
return _index < _items.Length;
}
public void Reset() => _index = -1;
public void Dispose() { /* Clean up if necessary */ }
}
Why Do This Manually?
While the compiler can often generate these for you, knowing how to build them manually is vital when your traversal requires stateful logic—such as filtering items on the fly, implementing custom "look-ahead" features, or managing complex memory pointers that need careful disposal. You are no longer just iterating; you are architecting the experience of consuming your collection.
Key Takeaway: The Enumerator Pattern transforms a passive collection into an active, stateful iterator, giving you complete command over the sequence and validity of data traversal.