Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Why does this foreach loop NOT throw the "collection was modified" exception?

I’m using .NET Framework 4.0.

The magic is somewhere within the OrderBy() LINQ method. Here’s a couple of examples for you:

var list = new List<int> { 1, 2, 3, 4, 5, 6};
foreach (var item in list)
{
    if (item % 2 == 0)
      list.Remove(item);
}

This loop, as expected, throws the "System.InvalidOperationException: Collection was modified; enumeration operation may not execute." exception.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

However if I add a call to OrderBy():

foreach (var item in list.OrderBy(v => v))
{
    if (item % 2 == 0)
      list.Remove(item);
}

the code executes just fine, removing all the even numbers from the list.

At first I assumed that OrderBy() just enumerated the source list and created a sorted copy of it. This would make sense and explain why the loop does not throw an exception: I’m not enumerating the same list as I’m modifying. However in the documentation ("Remarks" section) it is stated that:

This method is implemented by using deferred execution. The immediate return value is an object that stores all the information that is required to perform the action. The query represented by this method is not executed until the object is enumerated either by calling its GetEnumerator method directly or by using foreach in Visual C# or For Each in Visual Basic.

So is it an error in the documentation (maybe accidental copy-pasting of this block?) or did I miss something?

P.S. There’s this question, but the most upvoted answer assumes that OrderBy() just enumerated the list. I’m curious to know whether it’s true or not (a reference to some .NET source
is very welcome). Maybe the copy is indeed not created, but the source list is completely enumerated before I modify it?

>Solution :

I think this is because when the OrderBy starts executing it creates a copy of the list in the Buffer<T> class by calling ToArray, therefore modifying the original list doesn’t throw an exception. Here is a reference to the source code

 internal Buffer(IEnumerable<TElement> source)
    {
        if (source is IIListProvider<TElement> iterator)
        {
            TElement[] array = iterator.ToArray();
            _items = array;
            _count = array.Length;
        }
        else
        {
            _items = EnumerableHelpers.ToArray(source, out _count);
        }
    }

Buffer is initialized in the GetEnumerator method:

public IEnumerator<TElement> GetEnumerator()
    {
        Buffer<TElement> buffer = new Buffer<TElement>(_source);
        if (buffer._count > 0)
        {
            int[] map = SortedMap(buffer);
            for (int i = 0; i < buffer._count; i++)
            {
                yield return buffer._items[map[i]];
            }
        }
    }
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading