If I use:
var strings = new List { \"sample\" };
foreach (string s in strings)
{
Console.WriteLine(s);
strings.Add(s + \"!\");
}
It's because the ForEach
method doesn't use the enumerator, it loops through the items with a for
loop:
public void ForEach(Action<T> action)
{
if (action == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
}
for (int i = 0; i < this._size; i++)
{
action(this._items[i]);
}
}
(code obtained with JustDecompile)
Since the enumerator is not used, it never checks if the list has changed, and the end condition of the for
loop is never reached because _size
is increased at every iteration.
List<T>.ForEach
is implemented through for
inside, so it does not use enumerator and it allows to modify the collection.
Because the ForEach attached to the List class internally uses a for loop that is directly attached to its internal members -- which you can see by downloading the source code for the .NET framework.
http://referencesource.microsoft.com/netframework.aspx
Where as a foreach loop is first and foremost a compiler optimization but also must operate against the collection as an observer -- so if the collection is modified it throws an exception.
We know about this issue, it was an oversight when it was originally written. Unfortunately, we can't change it because it would now prevent this previously working code from running:
var list = new List<string>();
list.Add("Foo");
list.Add("Bar");
list.ForEach((item) =>
{
if(item=="Foo")
list.Remove(item);
});
The usefulness of this method itself is questionable as Eric Lippert pointed out, so we didn't include it for .NET for Metro style apps (ie Windows 8 apps).
David Kean (BCL Team)