Iterator Pattern and IEnumerable in C
This article explores the Iterator design pattern and how the .NET
framework integrates it natively through IEnumerable and
IEnumerator. You will learn the core concepts of the
Iterator pattern, how the C# compiler handles collection traversal
behind the scenes using the foreach loop, and how the
yield return statement allows developers to implement
custom iterators effortlessly without manually managing state
machines.
What is the Iterator Pattern?
The Iterator pattern is a behavioral design pattern that allows you to sequentially traverse elements of a complex data structure—such as lists, trees, or hash tables—without exposing its underlying representation (e.g., arrays, nodes, or buckets).
The pattern decouples the traversal algorithm from the collection container itself. Traditionally, it consists of two main components:
- Iterator: An object responsible for tracking the current position and advancing to the next element.
- Aggregate (Iterable): An interface that provides a method to produce an instance of the Iterator.
By separating these responsibilities, multiple iterators can traverse the same collection independently and simultaneously without altering the collection's internal data.
The C#
Abstraction: IEnumerable and IEnumerator
In C#, the Iterator pattern is standardized through two primary
interfaces located in the System.Collections.Generic
namespace:
IEnumerator<T>(The Iterator): Defines the mechanism for moving through the collection. It exposes:T Current { get; }: Returns the element at the current position.bool MoveNext(): Advances to the next item and returnstrue, or returnsfalseif the end of the collection is reached.void Reset(): Moves the pointer back before the first element (rarely used in modern C#).void Dispose(): Inherited fromIDisposableto release any unmanaged resources.
IEnumerable<T>(The Aggregate): Defines a single contract:IEnumerator<T> GetEnumerator(): Returns anIEnumerator<T>used to iterate through the collection.
How C# Implements the Pattern Implicitly
C# simplifies the Iterator pattern to the point where developers
rarely need to implement IEnumerator<T> manually.
This implicit implementation happens at two major levels: consumption
and creation.
1. Implicit
Consumption: The foreach Statement
When you iterate over an IEnumerable using a
foreach loop, the C# compiler translates that high-level
syntax into the low-level mechanics of the Iterator pattern at compile
time.
Consider this foreach loop:
List<int> numbers = new List<int> { 1, 2, 3 };
foreach (int number in numbers)
{
Console.WriteLine(number);
}The C# compiler lowers this code into the following equivalent logic:
List<int>.Enumerator enumerator = numbers.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
int number = enumerator.Current;
Console.WriteLine(number);
}
}
finally
{
enumerator.Dispose();
}The consumer never manages the pointer or the state directly; the compiler handles initialization, boundary checking, advancement, and resource disposal implicitly.
2. Implicit
Creation: The yield return Keyword
Historically, creating a custom iterator required writing a dedicated
class that implemented IEnumerator<T>, tracking index
positions, handling edge cases, and storing state.
C# eliminates this boilerplate using the yield return
keyword. When a method returns IEnumerable<T> or
IEnumerator<T> and contains yield, the
compiler automatically generates a hidden, private state machine class
behind the scenes.
public static IEnumerable<int> GetEvenNumbers(IEnumerable<int> source)
{
foreach (int num in source)
{
if (num % 2 == 0)
{
yield return num;
}
}
}When this method is called:
- Execution does not run the method body immediately. Instead, it
returns a compiler-generated object that implements
IEnumerable<int>andIEnumerator<int>. - Execution pauses at each
yield returnuntil the consumer callsMoveNext(). - The compiler-generated state machine preserves local variables and the execution pointer between calls, providing lazy evaluation without requiring the developer to manually write an iterator class.