| Keywords | IEnumerator, IEnumerable, yield return, yield break |
|---|---|
| Namespace | System.Collections |
| In Unity | Coroutines (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:
| Member | Meaning |
|---|---|
bool MoveNext() | "Run until the next yield return." Returns false when the method has finished. |
Current | The 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:
- Calling
Blink()runs none of its code. It just creates the iterator object. That's why calling a coroutine method withoutStartCoroutinedoes nothing. StartCoroutineimmediately callsMoveNext()once. Your code runs up to the firstyield return.- Unity looks at
Current:null→ callMoveNext()again next frameWaitForSeconds→ 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
- This repeats until
MoveNext()returnsfalse(the method reached its end oryield break), or the coroutine is stopped. - 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
IEnumerable | IEnumerator | |
|---|---|---|
| Is | Something you can loop over (a "sequence") | A cursor moving through a sequence |
| Used by | foreach, LINQ | Unity coroutines |
| Return this from | Iterators you'll foreach over | Coroutines |
Common Mistakes
- Returning
IEnumerablefrom a coroutine.StartCoroutinewants anIEnumerator. UseIEnumerator(fromSystem.Collections, not the generic one). - Thinking iterators are threads. Every piece runs on the main thread. A heavy loop between two yields still freezes the game.
yield returninside try/catch. Not allowed by C#. Atrywith onlyfinallyis allowed.returninstead ofyield break. Inside an iterator, useyield break;to stop early. A plainreturn value;won't compile.
See also
- Coroutines (Unity usage guide)
- async & await (the other way to wait)
- Collections
This page is a simplified, AI-assisted interpretation. For the complete and authoritative reference, see the official Microsoft C# documentation.