# Iterator

The Iterator pattern lets code walk through a collection without knowing how it's stored. In C#, foreach works on anything that implements IEnumerable&lt;T>, which hands out an IEnumerator&lt;T> with MoveNext and Current. Write one by hand to see how it works, then let yield return write it for you.

Journeyman · C# · OOP · Updated November 3, 2026

## After this lesson you can

- Make a class work with foreach by implementing IEnumerable&lt;T> with yield return

## Key takeaways

- `foreach` works on anything that implements `IEnumerable<T>`, whose `GetEnumerator()` hands out a new iterator each time.
- An iterator is an `IEnumerator<T>`: `MoveNext()` steps to the next item and returns `false` when there are no more; `Current` is the item it's on.
- `yield return` hands out one item and pauses until the loop asks for the next, so the compiler writes the iterator class for you.
- A class that implements `IEnumerable<T>` works with LINQ too, and can still keep its list private.

A `foreach` loop works on a `List`, an array, a `Dictionary`, a `string`, and the results of a LINQ query. They store their items in completely different ways, yet `foreach` doesn't care. That's the **Iterator** pattern: a way to go through a collection, one item at a time, **without knowing how it's stored**.

## A team that foreach can't loop over

A team holds at most four people. It keeps them in a private list, so nobody can add a fifth behind its back:

```csharp
var team = new Team();
team.Add(new Person("Ada", "Databases"));
team.Add(new Person("Grace", "Compilers"));

public record Person(string Name, string Skill);

public class Team
{
    private readonly List<Person> people = new List<Person>();

    public void Add(Person person)
    {
        if (people.Count == 4)
        {
            throw new InvalidOperationException("A team has at most four people.");
        }
        people.Add(person);
    }
}
```

Now try `foreach (Person person in team)`. It doesn't compile:

```text
error CS1579: foreach statement cannot operate on variables of type 'Team' because 'Team' does not contain a public instance or extension definition for 'GetEnumerator'
```

## What foreach needs

`foreach` needs the collection to give it an **iterator**: an object that walks through the items. C# describes both sides with two interfaces:

| Interface | Has | Means |
|---|---|---|
| `IEnumerable<T>` | `GetEnumerator()` | "You can loop over me": hands out a new iterator |
| `IEnumerator<T>` | `MoveNext()` and `Current` | The iterator: `MoveNext()` steps to the next item, and says `false` when there are no more; `Current` is the item it's on |

A `foreach` loop is shorthand for:

```csharp
IEnumerator<Person> iterator = team.GetEnumerator();
while (iterator.MoveNext())
{
    Person person = iterator.Current;
    // the loop's body
}
```

So, to make `Team` work with `foreach`, it implements `IEnumerable<Person>`.

## By hand

Here's an iterator written out in full, so you can see the parts:

```csharp
using System.Collections;

var team = new Team();
team.Add(new Person("Ada", "Databases"));
team.Add(new Person("Grace", "Compilers"));

foreach (Person person in team)
{
    Console.WriteLine($"{person.Name}: {person.Skill}");
}

public record Person(string Name, string Skill);

public class Team : IEnumerable<Person>
{
    private readonly List<Person> people = new List<Person>();

    public void Add(Person person)
    {
        if (people.Count == 4)
        {
            throw new InvalidOperationException("A team has at most four people.");
        }
        people.Add(person);
    }

    public IEnumerator<Person> GetEnumerator() => new TeamIterator(people);

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class TeamIterator : IEnumerator<Person>
{
    private readonly List<Person> people;
    private int position = -1;

    public TeamIterator(List<Person> people)
    {
        this.people = people;
    }

    public bool MoveNext()
    {
        position++;
        return position < people.Count;
    }

    public Person Current => people[position];

    object IEnumerator.Current => Current;

    public void Reset() => position = -1;

    public void Dispose() { }
}
```

```text
Ada: Databases
Grace: Compilers
```

- **`position`** starts at `-1`, before the first item. The first `MoveNext()` moves it to `0`.
- **`GetEnumerator`** makes a **new** iterator each time. That matters: two loops over the same team, even one inside the other, each need their own place in the list.
- The extra `IEnumerable.GetEnumerator` and `IEnumerator.Current` are there because the non-generic interfaces are older than generics; they just pass on to the real ones. `Reset` comes from that old interface too (`foreach` never calls it).
- **`Dispose`** comes from `IDisposable`: `foreach` calls it when the loop ends, so an iterator can clean up. This one has nothing to clean up.

It works, but it's a lot of code for "go through the list".

## With yield return

C# can write the iterator for you. In a method that returns `IEnumerator<T>` or `IEnumerable<T>`, **`yield return`** hands out one item, and **pauses** until the loop asks for the next:

```csharp
using System.Collections;

var team = new Team();
team.Add(new Person("Ada", "Databases"));
team.Add(new Person("Grace", "Compilers"));
team.Add(new Person("Linus", "Kernels"));

foreach (Person person in team)
{
    Console.WriteLine(person.Name);
}

Console.WriteLine(string.Join(", ", team.Where(p => p.Name.Length > 3).Select(p => p.Name)));

public record Person(string Name, string Skill);

public class Team : IEnumerable<Person>
{
    private readonly List<Person> people = new List<Person>();

    public void Add(Person person)
    {
        if (people.Count == 4)
        {
            throw new InvalidOperationException("A team has at most four people.");
        }
        people.Add(person);
    }

    public IEnumerator<Person> GetEnumerator()
    {
        foreach (Person person in people)
        {
            yield return person;
        }
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
```

```text
Ada
Grace
Linus
Grace, Linus
```

The compiler turns that method into an iterator class like `TeamIterator`, with its position and `MoveNext`, behind the scenes. And because `Team` is an `IEnumerable<Person>`, **LINQ** works on it too: `Where` and `Select` only need something they can loop over.

The list stays private. Callers can loop over the team, but they can't `Add` a fifth person to the list, or remove anyone: that's encapsulation and the Iterator pattern working together.

## Check yourself

1 question to try on the lesson page.

## So what

`foreach` works on anything that implements `IEnumerable<T>`. Implement it with `yield return`, and your own classes get `foreach` and LINQ, while keeping how they store their items to themselves.

## Key terms

- [Iterator](https://www.ask-codey.com/glossary/iterator-c-sharp)
- [IEnumerable&lt;T>](https://www.ask-codey.com/glossary/ienumerable-t)
- [IEnumerator&lt;T>](https://www.ask-codey.com/glossary/ienumerator-t)
- [Yield Return](https://www.ask-codey.com/glossary/yield-return)

## Cheat sheets

- [Iterator](https://www.ask-codey.com/cheat-sheets/design-patterns-iterator-cheat-sheet)

Source: https://www.ask-codey.com/lessons/design-patterns-iterator
