Understanding Enumerators and Iterators in C#

How foreach Works Under the Hood

The foreach statement in C# provides a convenient way to iterate through collection elements:

string[] colors = { "Red", "Green", "Blue" };

foreach (string color in colors)
    Console.WriteLine($"Color: {color}");

Output:

Color: Red
Color: Green
Color: Blue

While this syntax appears simple, it's actually syntactic sugar that the compiler transforms into more explicit code. The equivalent implementation without foreach looks like this:

static void Main()
{
    string[] colors = { "Red", "Green", "Blue" };
    
    IEnumerator iterator = colors.GetEnumerator();
    
    while (iterator.MoveNext())
    {
        string color = (string)iterator.Current;
        Console.WriteLine($"Color: {color}");
    }
}

Both approaches produce identical output. The foreach loop compiles down to code that explicitly calls GetEnumerator(), MoveNext(), and Current.

The mechanism works like this: calling GetEnumerator() on a collection returns an enumerator—an instance of a class implementing IEnumerator. Collections that implement IEnumerator are called enumerable types. Arrays are enmuerable types because they inherit IEnumerator GetEnumerator() through the IEnumerable interface.

The IEnumerator Interface

The IEnumerator interface defines the contract for objects that can enumerate through a collection. Here's its structure:

namespace System.Collections
{
    public interface IEnumerator
    {
        object Current { get; }
        
        bool MoveNext();
        
        void Reset();
    }
}

The interface consists of three members:

Current Property A read-only property that returns the element at the enumerator's current position. It returns an object reference, allowing any type to be returned.

MoveNext Method Advances the enumerator to the next element. Returns true if the new position is valid, false if the enumerator has passed the end of the collection. Note that the enumerator starts positioned before the first element, so MoveNext() must be called before accessing Current for the first time.

Reset Method Repositions the enumerator to its initial position, which is before the first element in the collection.

Building a Custom Enumerator

Here's a custom enumerator class demonstrating the standard pattern for implementing IEnumerator:

using System;
using System.Collections;

class SeasonEnumerator : IEnumerator
{
    private string[] items;
    private int index = -1;

    public SeasonEnumerator(string[] source)
    {
        items = new string[source.Length];
        for (int i = 0; i < source.Length; i++)
        {
            items[i] = source[i];
        }
    }

    public object Current
    {
        get
        {
            if (index == -1)
                throw new InvalidOperationException("Enumeration has not started");
            if (index >= items.Length)
                throw new InvalidOperationException("Enumeration has ended");
            return items[index];
        }
    }

    public bool MoveNext()
    {
        if (index < items.Length - 1)
        {
            index++;
            return true;
        }
        return false;
    }

    public void Reset()
    {
        index = -1;
    }
}

The key observations about this implementation:

The enumerator maintains state through the index field. When index is -1, the enumerator is in its initial state. The MoveNext() method increments index and returns true as long as a valid element exists. Once index reaches the array length, MoveNext() returns false and subsequent calls to Current throw exceptions.

The enumerator stores a private copy of the collection (items) rather than a reference to the original. This design choice prevents issues if the original collection is modified after the enumerator is created.

The IEnumerable Interface

An enumerable type implements IEnumerable, which exposes its enumerator through a single method:

namespace System.Collections
{
    public interface IEnumerable
    {
        IEnumerator GetEnumerator();
    }
}

Building a Custom Enumerable Type

Here's how to create a custom enumerable class:

using System.Collections;

class Seasons : IEnumerable
{
    private string[] data = { "Spring", "Summer", "Autumn", "Winter" };

    public IEnumerator GetEnumerator()
    {
        return new SeasonEnumerator(data);
    }
}

With this implementation, you can now use the custom collection with foreach:

Seasons seasons = new Seasons();

foreach (string season in seasons)
{
    Console.WriteLine(season);
}

The GetEnumerator() method instantiates and returns the appropriate enumerator type, connecting the enumerable type to its enumeration logic.

Tags: C# enumerators iterators IEnumerable IEnumerator

Posted on Wed, 09 Sep 2026 16:09:17 +0000 by ekosoftco