All C# developers faced these interfaces at some point, but how to use them? And better than that, how to use them properly? — Because I’ve seen a lot of mistakes with these guys.
The main point of writing this article is the fact that I lost the count about how many times I’ve seen mistakes and misunderstoods related to C# collections, so I hope to clarify some points here.
Before we start, let’s go a little back and see the cover image at the beginning of the text. It’s a visualization about how you may thinking about
It works like a Turing Machine tape with a cursor that start pointing to
null. This cursor move only straight forward (to right in the image), without index access and besides that, only the necessary information to iterate the collection is provided by an
Having said that, we can start to code. Let’s check the
IEnumerable implementation (with and without generics):
And that’s all. There’s no one more line code at
What does this suggest?
IEnumerable actually is a
IEnumerator factory. Now we need to check
IEnumerator out. It’s a little bit more complex, but we can handle it.
There’s two different interfaces, with and without using generics, just like
IEnumerator is the one who provides the information about how to iterate a collection (and
IEnumerable that is the famous one, how unfair is that?).
In fact, when we use the
foreach loop, C# internally calls the
GetEnumerator of the collection we’ll iterate and at each step it calls the
MoveNext method to move the cursor forward.
Let’s code a super simple code to iterate an IEnumerable of integers using a foreach loop:
It’s pretty simple, isn’t?
As I said before, this code is a simple syntax sugar to another code a little bit more complex, but still a simple code snippet:
This code is a lot more uglier but it does the exactly same thing. The first step got the enumerator using
GetEnumerator method, after that, we can use the
MoveNext method to iterate each of
It’s interesting to notice that the
MoveNext method is called even before we got the first element. It occurs because the enumerator cursor start pointing to one position before the first element (as shown in the tape image).
MoveNext method have a design that I particularly don’t like. It does two different things.
- One of these things is to move the cursor to the next position, which is the main purpose;
- The secondary thing is what this method returns. It returns
trueif the cursor is pointing to a valid position after it was moved or it will return
My problem with the design of this method is the fact that the main operation, move the cursor to the next element, is caused only by a side effect, but this is a topic for another article, let’s move on.
After call the
MoveNext method the first time, we’ll move the cursor to the very first position of the
IEnumerable, like the image below:
And the property
Current just returns the value at the position that the cursor points to.
Having said that, how about to create a brand new collection containing all integer positive numbers that exists?
Yeah, you read it correctly, a collection with all numbers, which means, a infinity collection.
Here’s a thing before we start to code, the whole point of this implementation is see how
IEnumerableworks under the hood with a
foreachloop, so, don’t take it as the best implementation possible for a new collection, it’s just an experiment, right?
Let’s start with an
IEnumerator, so, just create a new class that implements
IEnumerator<int>, It will looks like the code below:
So, now we need to create our own stuff now. In order to create an infinite collection, we’ll use the cursor itself as a value.
Let’s create a new field called
_current which starts at
-1 and will be incremented as the
MoveNext method is called (usually the value is stored at the
IEnumerable, but this isn’t a regular example, remember?)
Ok, but let’s understand why
_current starts with
-1 instead of zero. The answer is pretty simple, it’s because the
MoveNext method is called before the iteration.
Now, let’s go to
MoveNext method, you probably already know what to do here. We need to increase the
_current value and always return true, after all, it’s an infinite collection, so always is possible to move to the next one.
We don’t need to worry about the
Dispose method now, so let’s implement the
Reset method by reseting the
_current to its initial value.
Now, let’s implement our collection itself. It’ll be easy, trust me. All we need is implement the
IEnumerable<int> interface and return an instance of our
Maybe for some reason, you may need return new instances any time you call
GetEnumerator, but for our example, only one instance is enough.
Now we can use our own class inside of a
And we can get the next, the next, and the next and keep doing that forever.
How cool is that?
So, everything was implemented correctly, right? — Not exactly.
Let’s force an interruption by using
BANG! — An exception was thrown.
Why this exception was thrown? — Simple, the
Dispose method wasn’t implemented yet.
Right, but when it’s called?
Just after the loop! In order to remember, let’s reimplement it without
foreach sugar syntax, in other words, by using
Now the things are more clear, aren’t they?
When we iterate across an
IEnumerable we use the
using keyword, and as you probably already know, the
Dispose method is called at the end of the using scope.
The reason why the
Dispose method is called is a lot more clear with this syntax, even though it looks worse. We can fix it just by calling
Reset method at
This isn’t the best way to implement the
Disposemethod whatsoever, this is just an experiment, ok?
Now, you have your own infinite collection, it’s time to snapping your fingers (Like Thanos did)!
See you in the next post!
Top comments (0)