What is the difference between IEnumerator and IEnumerable?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
IEnumerable and IEnumerator are closely related in .NET, but they represent different roles in iteration. IEnumerable is the thing you can iterate over, while IEnumerator is the stateful cursor that actually walks through the elements one by one.
IEnumerable: The Sequence
A type that implements IEnumerable promises that it can provide an enumerator. That is why foreach works on it.
Here the collection itself is not the thing remembering the current position. It only knows how to create an enumerator when iteration begins.
That design matters because you can ask for a fresh enumerator more than once. Each foreach loop gets its own walk through the sequence.
IEnumerator: The Cursor
IEnumerator is the object that tracks where iteration currently is. It exposes members such as MoveNext() and Current.
This is roughly what foreach does for you automatically. The enumerator starts before the first element, MoveNext() advances it, and Current exposes the current item.
Because the enumerator holds iteration state, it is usually single-use. Once it has reached the end, you do not normally rewind and reuse it in ordinary application code.
How They Work Together
The easiest mental model is:
- '
IEnumerableanswers "can I start iterating this thing?"' - '
IEnumeratoranswers "where am I right now during this iteration?"'
A List<T> is enumerable. The object returned by list.GetEnumerator() is the enumerator. foreach hides that distinction most of the time, which is why the interfaces feel confusing until you look at them side by side.
The generic forms IEnumerable<T> and IEnumerator<T> are the versions you should usually use in modern C# because they provide type safety.
Why This Split Is Useful
Separating the sequence from the cursor makes iteration composable. A collection does not need to store one global position for everybody. Multiple callers can enumerate the same sequence independently, and lazy sequences can generate items only as the enumerator asks for them.
That is also why yield return is so powerful. The compiler creates the enumerator machinery for you, which lets you expose IEnumerable<T> without manually implementing the full cursor class in many cases.
Once you see that split clearly, a lot of .NET collection behavior becomes easier to understand. LINQ sequences, custom iterators, and foreach all rely on the same basic contract: ask the sequence for a cursor, then advance that cursor until it runs out.
That is a small conceptual distinction, but it shows up everywhere in idiomatic C# code.
Once it clicks, iterator-based APIs stop feeling mysterious.
Common Pitfalls
- Treating
IEnumerableas if it were already positioned on a current element. - Reusing an exhausted enumerator instead of requesting a new one.
- Implementing the non-generic interfaces when the generic ones are more appropriate.
- Forgetting that
foreachis using an enumerator under the hood. - Assuming the collection itself stores one shared iteration position.
Summary
- '
IEnumerablerepresents something that can be iterated.' - '
IEnumeratorrepresents the stateful cursor for one iteration.' - '
foreachobtains an enumerator from an enumerable and drives it automatically.' - Generic versions are preferred in modern C#.
- Think sequence versus cursor and the distinction becomes much clearer.

