How do I implement IEnumerableT
Object-Oriented Design practice on Codemia
Turn requirements into classes, and defend the design, on the problems that come up in OOD rounds.
Introduction
Implementing IEnumerable<T> in C# is what allows your custom collection to work with foreach, LINQ, and the broader .NET iteration ecosystem. The key requirement is simple: your type must provide a generic enumerator, and it should also satisfy the non-generic IEnumerable contract for compatibility.
The Minimal Contract
To implement IEnumerable<T>, your class must provide:
- '
IEnumerator<T> GetEnumerator()' - '
IEnumerator IEnumerable.GetEnumerator()'
The non-generic method is usually implemented explicitly and forwards to the generic one.
A practical custom collection looks like this:
That is enough for this code to work:
Why yield return Is So Useful
yield return is the easiest way to implement enumeration because the compiler generates the state machine for you. You do not have to manually build an enumerator class unless you need a very specialized iteration pattern.
That makes most custom collection implementations short and readable.
If your type already wraps another collection internally, forwarding with yield return or returning the inner enumerator is usually the cleanest design.
A More Direct Enumerator Forwarding Pattern
If you do not need custom iteration logic, you can return the underlying collection’s enumerator directly:
This is more concise than yield return when the wrapped collection already behaves exactly the way you want.
When You Need a Custom Enumerator
Sometimes iteration is not just "walk through a list." You may need to:
- skip null or invalid items
- expose a computed sequence
- traverse a tree or graph
- iterate lazily over generated values
In those cases, yield return still works very well.
Example with filtering:
Now the collection exposes only non-negative values through enumeration, even if it stores more internally.
Relationship to ICollection<T> and IReadOnlyCollection<T>
IEnumerable<T> only promises iteration. It does not promise count, indexing, add, remove, or mutation behavior.
If your type is a full collection, you may also want to implement:
- '
ICollection<T>' - '
IReadOnlyCollection<T>' - '
IList<T>orIReadOnlyList<T>'
But you do not need those interfaces just to support foreach.
Common Pitfalls
The biggest mistake is implementing only the generic GetEnumerator() and forgetting the non-generic IEnumerable.GetEnumerator(). Some older APIs and non-generic consumers still rely on that interface.
Another issue is exposing an enumerator over a collection that can be modified unsafely during iteration. If the underlying storage changes, enumeration behavior may become invalid or throw.
Developers also sometimes implement IEnumerable<T> on a type that is really a one-shot stream rather than a reusable collection. That can confuse callers who expect enumeration to be repeatable.
Finally, do not write a custom enumerator class unless you truly need it. In most cases, yield return is simpler and less error-prone.
Summary
- Implement both the generic and non-generic enumeration methods.
- Use
yield returnfor the simplest and most readable custom iteration logic. - Forward to an inner collection enumerator when that already matches the desired behavior.
- '
IEnumerable<T>means "can be iterated," not necessarily "full mutable collection."' - Keep enumeration behavior predictable, especially when underlying data can change.
Related reading
- How do I make the return type of a method generic?
- How do I prevent the modification of a private field in a class?
- How do you effectively model inheritance in a database?
- How do you find all subclasses of a given class in Java?
- How do I improve ASP.NET MVC application performance?
- How do I invert BooleanToVisibilityConverter?
- How do you implement the Singleton design pattern?
- How does inheritance work for Attributes?

OOD Fundamentals
Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Object-Oriented Design practice on Codemia
Turn requirements into classes, and defend the design, on the problems that come up in OOD rounds.