Iterators
KeywordsIEnumerator, IEnumerable, yield return, yield break
NamespaceSystem.Collections
In UnityCoroutines (StartCoroutine)

Iterators & yield (Coroutines)

Unity coroutines are built on a plain C# feature called iterators. An iterator is a method that uses yield return to hand back values one at a time, pausing in between. Understanding this C# side explains how coroutines really work. For the Unity usage guide, see Coroutines.

A Pez dispenser. A normal method that returns a list dumps all the candy out at once. An iterator hands out one candy each time you push the head (MoveNext()), and it remembers how many are left. Unity pushes the head of your coroutine once per frame (or whenever the wait is over).

Iterators in plain C#

Here's an iterator with nothing to do with Unity:

using System.Collections.Generic;

IEnumerable<int> CountToThree()
{
    Debug.Log("start");
    yield return 1;       // hand back 1, pause here
    Debug.Log("after 1");
    yield return 2;
    Debug.Log("after 2");
    yield return 3;
    Debug.Log("done");
}

foreach (int n in CountToThree())
{
    Debug.Log(n);
}
// Prints: start, 1, after 1, 2, after 2, 3, done

The method's code runs in pieces: it runs until a yield return, hands back a value, and only continues when the caller asks for the next one.

Driving an iterator by hand

foreach hides the details. Underneath, it uses an IEnumerator with two members:

MemberMeaning
bool MoveNext()"Run until the next yield return." Returns false when the method has finished.
CurrentThe value from the latest yield return.
IEnumerator<int> e = CountToThree().GetEnumerator();
while (e.MoveNext())       // run the next piece
{
    Debug.Log(e.Current);  // the value it yielded
}

How Unity uses this for coroutines

A coroutine is an iterator that returns IEnumerator. The values you yield (null, new WaitForSeconds(1)) are instructions to Unity about when to call MoveNext() again.

IEnumerator Blink()
{
    while (true)
    {
        rend.enabled = !rend.enabled;
        yield return new WaitForSeconds(0.5f); // Current = a WaitForSeconds object
    }
}

StartCoroutine(Blink()); // Unity gets the IEnumerator and drives it

What Unity does, simplified:

  1. Calling Blink() runs none of its code. It just creates the iterator object. That's why calling a coroutine method without StartCoroutine does nothing.
  2. StartCoroutine immediately calls MoveNext() once. Your code runs up to the first yield return.
  3. Unity looks at Current:
    • null → call MoveNext() again next frame
    • WaitForSeconds → call it again once that much game time has passed
    • another Coroutine → call it again when that one finishes
    • WaitUntil → check the condition every frame, then call it again
  4. This repeats until MoveNext() returns false (the method reached its end or yield break), or the coroutine is stopped.
  5. Local variables survive between pieces because C# turns your method into a hidden class that stores them as fields.

Writing your own iterators

Iterators are useful outside coroutines too, for example producing values lazily:

// Every grid cell around a point, without building a list
IEnumerable<Vector2Int> Neighbours(Vector2Int c)
{
    for (int x = -1; x <= 1; x++)
    for (int y = -1; y <= 1; y++)
    {
        if (x == 0 && y == 0) continue;
        yield return new Vector2Int(c.x + x, c.y + y);
    }
}

foreach (Vector2Int n in Neighbours(playerCell))
{
    if (IsWall(n)) continue;
    // ...
}

IEnumerator vs. IEnumerable

IEnumerableIEnumerator
IsSomething you can loop over (a "sequence")A cursor moving through a sequence
Used byforeach, LINQUnity coroutines
Return this fromIterators you'll foreach overCoroutines

Common Mistakes

See also