Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does List<T>.ForEach allow its list to be modified?

Tags:

c#

foreach

list

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)

like image 850
SWeko Avatar asked Feb 16 '12 13:02

SWeko


People also ask

Can we modify List in foreach?

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.

Does foreach work for List?

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.

Why is foreach loop read only?

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.

How to iterate through collection in C#?

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.


1 Answers

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.

like image 60
Thomas Levesque Avatar answered Oct 09 '22 21:10

Thomas Levesque