Today, we’re taking a deeper look at foreach
loop in C#. What does a collection need to be able to use it in a foreach
loop? Does it have to implement IEnumerable
interface? These questions are often asked during interviews, so it’s worth knowing the answers ?
We will go through a step-by-step example in building our own custom collection to see how all that works. Let’s dive in! ?
Throughout this article, I’m working with a Unit Tests project using .NET 5, C# 9.0 and NUnit. It makes it easier to play with the code on your own. We can create our custom collection and at the same time try it out in a foreach
loop in a test method. I recommend this approach for testing out various programming concepts for yourself ?
Custom collection implementing IEnumerable<T>
First, we want to implement our own, custom collection in C#. The most obvious solution is to implement IEnumerable<T>
interface. Our IDE will require our class to implement IEnumerator GetEnumerator()
and IEnumerator IEnumerable.GetEnumerator()
methods, so let’s do that:
Of course, this is just an example to satisfy the foreach
loop, so we won’t care about the actual implementation of these methods ?
Custom collection implemented in such a way can be used in foreach
loop:
Does foreach require IEnumerable?
Let’s try to get rid of implementing IEnumerable
interface. As soon as we do it, our IDE complains:
Let’s try to remove IEnumerable.GetEnumerator()
method completely, so our custom collection looks as follows:
Now guess what – we can still use MyCollection
within the foreach
loop!
So you already know that, when asked during an interview whether foreach
needs a collection to implement IEnumerable
, the answer is no ?
Custom IEnumerator
Currently, our collection has a single GetEnumerator()
method returning IEnumerator<MyObject>
. We already know that this method is needed in a collection to be able to use it within a foreach
loop.
However, let’s work on what this method returns. We’ll implement our custom IEnumerator
. Again – the most obvious solution is to create a class implementing IEnumerator<T>
interface:
That’s the implementation proposed by the IDE (Rider). We can now change our collection to use this custom enumerator. See that it can still be used within foreach
:
So far, so good. But why all these methods in MyObjectEnumerator
? ?
Does foreach need an enumerator implementing IEnumerator?
If we remove inheritance from IEnumerator
from MyObjectEnumerator
, the IDE only complains about IEnumerator.Current
property, so let’s get rid of it. Our custom enumerator now looks as follows:
Yeah, you guessed it again – we can still use our collection in a foreach
loop ?
However… Does our custom enumerator really needs all of these stuff: MoveNext(), Reset()
, Dispose()
and MyObject Current
? Let’s remove all of them and see what our IDE tells us:
That’s the error from IDE when trying to iterate through our custom collection in foreach
:
Type 'ForeachFun.MyCollection’ cannot be used in 'foreach’ statement because it neither implements 'IEnumerable’ or 'IEnumerable’, nor has suitable 'GetEnumerator’ method which return type has 'Current’ property and 'MoveNext’ method
This error actually tells us everything. This is the ultimate answer to the legendary C# interview question: „What does a collection need to be able to use it in foreach
loop?”. In other words: „Is it necessary for a collection to implement IEnumerable
to use it in a foreach
loop?”
The answer is: a collection needs to have a GetEnumerator
() method returning an object of type having Current
property and bool MoveNext()
method. No interfaces are required anywhere here ?
The compiler uses duck typing to find these two necessary ingredients of the type returned from GetEnumerator()
. It means that is looks for these properties by text. Later, depending on the type of Current
property, a single element inside the foreach
is typed accordingly. Only if this duck searching fails, the compiler checks whether the collection implements the interfaces.
In the end, this is our simplest and fully legit code (except the actual implementations of the methods, which is out of scope of this article):
Summary
I hope you got to know something new about foreach
loop from this article. This may not sound like a knowledge you’d need on a daily basis, but it’s worth knowing how stuff works under the hood ? Plus, you can always score a point during your next interview ?!