A bit of a long answer, the two previous answers cover most of it, but I found some aspects I found interesting when looking up foreach in the C#
language specification. Unless you are interested in that, stop reading.
Now over to the intering part, according to C#
spec expansion of the following statements:
foreach (V v in x) embedded-statement
Gves you:
{`
E e = ((C)(x)).GetEnumerator();
try {
while (e.MoveNext()) {
V v = (V)(T)e.Current;
embedded-statement
}
}
finally {
… // Dispose e
}
}
Having some kind of identity function which follows x == ((C)(x)).GetEnumerator()
(it is it's own enumerator) and using @JonSkeet's loops produces something like this (removed try/catch for brevity):
var list = new List<int> { 1, 2, 3 };
while (list.MoveNext()) {
int x = list.Current; // x is always 1
while (list.MoveNext()) {
int y = list.Current; // y becomes 2, then 3
Console.WriteLine("{0} {1}", x, y);
}
}
Will print something along the lines of:
1 2
1 3
And then list.MoveNext()
will return false
forever. Which makes a very important point, if you look at this:
var list = new List<int> { 1, 2, 3 };
// loop
foreach (var x in list) Console.WriteLine(x); // Will print 1,2,3
// loop again
// Will never enter loop, since reset wasn't called and MoveNext() returns false
foreach (var y in list) Console.WriteLine(x); // Print nothing
So with the above in mind, and note that it is totally doable since the foreach
statement looks for a GetEnumerator()
-method before it checks whether or not the type implements IEnumerable<T>
:
Why was the above approach not followed and the problems I will face
if I follow it?
You cannot nest loops, nor can you use the foreach
statement to access the same collection more than once without calling Reset()
manually between them. Also what happens when we dispose of the enumerator after each foreach
?
How does presence of IEnumerator interface solves those problems?
All iterations are independent of each other, whether we are talking nesting, multiple threads etc, the enumeration is separate from the collection itself. You can think of it as a bit like separations of concerns, or SoC, since the idea is to separate the traversal from the actual list itself and that the traversal under no circumstances should alter the state of the collection. IE a call to MoveNext()
with your example would modify the collection.