If I use:
var strings = new List<string> { "sample" }; foreach (string s in strings) { Console.WriteLine(s); strings.Add(s + "!"); }
the Add
in the foreach
throws an InvalidOperationException (Collection was modified; enumeration operation may not execute), which I consider logical, since we are pulling the rug from under our feet.
However, if I use:
var strings = new List<string> { "sample" }; strings.ForEach(s => { Console.WriteLine(s); strings.Add(s + "!"); });
it promptly shoots itself in the foot by looping until it throws an OutOfMemoryException.
This comes as a suprise to me, as I always thought that List.ForEach was either just a wrapper for foreach
or for for
.
Does anyone have an explanation for the how and the why of this behavior?
(Inpired by ForEach loop for a Generic List repeated endlessly)
Modify a C# collection with foreach by using a second collection. Since we cannot change a collection directly with foreach , an alternative is to use a second collection. To that second collection we then add new elements. This way we can still benefit from the simplicity that foreach offers.
Using the code. The ForEach method of the List<T> (not IList<T> ) executes an operation for every object which is stored in the list. Normally it contains code to either read or modify every object which is in the list or to do something with list itself for every object.
That is because foreach is meant to iterate over a container, making sure each item is visited exactly once, without changing the container, to avoid nasty side effects.
An iterator can be used to step through collections such as lists and arrays. An iterator method or get accessor performs a custom iteration over a collection. An iterator method uses the yield return statement to return each element one at a time.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With