Collection was modified; enumeration operation may not execute

This was a weird error I came across recently with a very simple solution, that came about because I tried to do something that really I should have known I couldn’t do. Often the way that the most irritating problems look incredibly simple in hindsight and you wonder how you could have been inattentive enough to fall into them.

Personally, i’m blaming fatigue, but I always use that excuse.

This error message, “Collection was modified; enumeration operation may not execute”, comes about in foreach loops.  Generally it occurs if you try and modify a collection’s references while using foreach to enumerate through it.

An example:

// Assume this value has been populated with a series of true and false values
List<bool> booleanValues;

// Attempting to remove all false’s from the list
foreach(bool b in booleanValues)
{
if (!b)
booleanValues.remove(b);
}

This code should generate the error, as it modifies the collection’s references whilst looping, thus disturbing the progression of the enumerator.

The simplest way around this involves using two loops, one to find the necessary values and the other to remove them, as shown.

List<bool> booleanValues;
List<bool> valuesToDelete = new List<bool>();

foreach(bool b in booleanValues)
{
if (!b)
valuesToDelete.add(b);

}

foreach(bool b in valuesToDelete)
{
booleanValues.remove(b);
}

Edit:

If you’re interested in the performance discussion that took place in the comments below, some more good testing and discussion on this was done at Schnieds blog:

http://www.schnieds.com/2009/03/linq-vs-foreach-vs-for-loop-performance.html

Advertisements

35 thoughts on “Collection was modified; enumeration operation may not execute

    • Not sure that I would call that ‘better’ for two main reasons Michael.

      Firstly, you’re calling count on every iteration, which on a large loop will eat up a large amount of processing cycles. Calling “count” on an object is a far more expensive operation than just checking a variable, or iterating through a list.

      Also, i’m not sure that it would work. Assume for a moment that i is 10. You remove the the object at i (index 10) and the list reshuffles so now the next object is at object 10. You then move on to check object 11 and completely skip the processing of the object that was next in line after the deleted one.

      I havn’t got my documentation here so I cannot check the functionality of RemoveAt, but that would definately be my guess as to what would happen. The real point of this posting is to show that -any- deletion of objects while iterating through the list like that is fundamentally a bad idea.

  1. Sorry yes, you need to decrease i by 1 when an item is removed from the list to avoid skipping the next value.

    for (int i = 0; i <= booleanValues.Count – 1; i++)
    {
    if (!booleanValues[i])
    {
    booleanValues.RemoveAt(i);
    i–;
    }
    }

    One this is in place however the RemoveAt() method is much faster.
    Start a new a new console application and try this

    My code took about 10 seconds and yours took nearly a minute and a half.

    using System;
    using System.Collections.Generic;

    namespace LoopTest
    {
    internal class Program
    {
    private static void Main(string[] args)
    {
    DateTime startTime;
    List booleanValues;

    booleanValues = GetBooleanList();

    startTime = DateTime.Now;
    for (int i = 0; i <= booleanValues.Count – 1; i++)
    {
    if (!booleanValues[i])
    {
    booleanValues.RemoveAt(i);
    i–;
    }
    }

    Console.WriteLine(string.Format("Bool Count = {0}, Time = {1}", booleanValues.Count,
    DateTime.Now.Subtract(startTime)));
    booleanValues = GetBooleanList();

    startTime = DateTime.Now;
    List valuesToDelete = new List();
    foreach (bool b in booleanValues)
    {
    if (!b)
    valuesToDelete.Add(b);
    }
    foreach (bool b in valuesToDelete)
    {
    booleanValues.Remove(b);
    }

    Console.WriteLine(string.Format(“Bool Count = {0}, Time = {1}”, booleanValues.Count,
    DateTime.Now.Subtract(startTime)));
    Console.Write(“\nPress any key to continue:”);
    Console.Read();
    }

    private static List GetBooleanList()
    {
    List values = new List();
    for (int i = 0; i <= 100000; i++)
    {
    values.Add(true);
    }
    for (int i = 0; i <= 100000; i++)
    {
    values.Add(false);
    }
    for (int i = 0; i <= 100000; i++)
    {
    values.Add(true);
    }
    for (int i = 0; i <= 100000; i++)
    {
    values.Add(false);
    }
    return values;
    }
    }
    }

  2. If you change the line
    foreach(bool b in booleanValues)
    to
    foreach(bool b in booleanValues.ToArray())

    then you should be able to remove the values from the list in this foreach loop (thanks to stackoverflow.com for this hint).

  3. Thanks Frater. I had the same problem and the explanation you did is great. But since I only need to delete one from the collection, using foreach loop will work. Just call the “break” statement after calling the Remove method. This will stop the iteration of the collection and error will not appear anymore. Thanks again.

    foreach (string s in PacketList)
    {
    if (s.IndexOf(sTag) == 1)
    {
    PacketList.Remove(s);
    break;// without this the exception will be thrown
    }
    }

  4. “Calling “count” on an object is a far more expensive operation than just checking a variable, or iterating through a list.”

    is not true because upon closer inspection of the disassembly of List I noticed Count is simply a property with backing field that is evaulated in Add(), Remove(), RemoveAt() etc.

    public int Count
    {
    get
    {
    return this._size;
    }
    }

  5. While I’m working in calenders dayrender event, in dynamically creating a linkbutton this error occurs.

    if (e.Day.Date.Month == 1 && e.Day.Date.Day == 26)
    {
    LinkButton Link = new LinkButton();
    Link.Text = “Event”;
    Link.ID = “LinkButton1”;
    Link.Click += new EventHandler(LinkButton1_Click);
    }

  6. The Data Access Application Block component includes the Microsoft Visual Basic .NET source code or the Microsoft Visual Basic 2005 source code and the Microsoft Visual C# .NET source code or the Microsoft Visual C# 2005 source code for the Data Access Application Block. It also includes a Quick Start Samples client application in each language that you can use to test common scenarios. The sample can help you to better understand how the Data Access Application Block works. You can also customize the source code to fit your requirements.

  7. Thank you so much for this, been hitting my head on the desk for about 3 hours now trying to figure out how to get past this.

  8. It’s amazing to me that a post I did years ago is still consistently amongst my top-viewed. I’d like to thank everyone who has posted on here with suggestions, updates and other ideas!

  9. Here’s the really elegant way of doing this:

    List ArrayList = Collection.ToList();
    for(int j = ArrayList.Count – 1; j >= 0; j–) {
    ArrayList.RemoveAt(j); //or some delete etc.
    }

    Volia you’re done and it works and is fastest of all because you’re not having to decrement the variable and it’s easy to read.

  10. you can always count backwards during the for loop which elimantes the problem also. removing them from the list from the back.

    use
    for (int i = booleanValues.Count – 1; i>-1 <= i–)
    {
    if (!booleanValues[i])
    booleanValues.RemoveAt(i);
    }

    instead of
    for (int i = 0; i <= booleanValues.Count – 1;i++)
    {
    if (!booleanValues[i])
    booleanValues.RemoveAt(i);
    }

    lekker.

  11. It was a line !!!

    I had this error been trying to find it for several days. It didn’t occur on my Dev machine but on test machine, and client machines. Whenever I closed a certain form – only that one form. I ended up commenting out the code…not the code. Started deleting controls off my form… it ended up being a line. A simple line !!!
    (Microsoft.VisualBasic.PowerPacks.LineShape)

    VS2010 pro
    XP/Vista/7
    MySQL Community Server

  12. The only Issue with using something like ToList() or ToArray() is both those methods have to enumerate the entire collection to insert the items into a new list or array.

    For very large lists my original For loop and RemoveAt(index) will achieve the same result with minimal processing.

    Can either count upwards and decrement the list upon removal like I did or as James and Heralt suggested counting down within the loop itself but for large lists or methods that are repeatedly called in realtime I would try to eliminate any superfluous iterations.

    • my code is i got same error .
      CachedWorksets.Add(workset);
      CachedWorksets = CachedWorksets.Distinct().ToList();

      StringBuilder builder = new StringBuilder();
      foreach (string Batch in CachedWorksets.Distinct()) // Loop through all strings
      {
      builder.Append(Batch).Append(“,”); // Append string to StringBuilder
      }

      // Get string from StringBuilder
      //for (int i = 0; i < CachedWorksets.Count – 1;i++ )
      //{
      // twork = ",";
      twork = builder.ToString();
      //}

  13. Realy Helpful. But we can also make it with one loop

    foreach(bool b in booleanValues.ToList())
    {
    booleanValues.Remove(b);
    }

  14. Pingback: [RESOLVED]Collection was modified; enumeration operation may not execute | ASP Questions & Answers

  15. I ran into the same problem and thought the same thing, “Why did I think this would be okay?”. I’ll post my solution which is also pretty much the same thing.

    foreach (var item in list.ToList())
    {
    if (!item)
    list.Remove(item);
    }

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s