0

I went through many questions related to the scenario of enumerating through a collection using foreach which results in this exception:

collection was modified enumeration operation may not execute exception

Most of the time I see people suggesting to use a for loop instead of foreach loop.

Is there any specific reason to use a for loop to fix the problem? Or is there any better way to fix this kind of scenario?

Yuck
  • 49,664
  • 13
  • 105
  • 135
unique
  • 5,442
  • 6
  • 23
  • 26
  • are you removing anything from collection? – Neel Jul 03 '14 at 06:28
  • 3
    You need to fix your question. As it is, it is UN-answerable. Post some code. I can imagine that you get this error because you iterating mutating collection. You can iterate mutating collection only using `while`, and not `loop` – T.S. Jul 03 '14 at 06:28
  • With a for loop you can modify what the next iterated element will be, a foreach you cannot – Sayse Jul 03 '14 at 06:29
  • 1
    [Related (and possible duplicate?)](http://stackoverflow.com/q/759966/1324033) – Sayse Jul 03 '14 at 06:30

7 Answers7

1

When you are working on collection used up in foreach loop, operation like adding new item or removing item should not be done, which modifies the collection.

But when we do it the following way it works well

List<string> list = new List<string>();

foreach (string str in list.ToList())
{
    list.Remove(str);
}

But when you do this, please note that .ToList() will actually create a new list which makes it work well. Creating new lists every time may not be a good practice.

Be wise when you implement the above code, or to keep it simple, just use a for loop instead.

Naga Sreenivas
  • 312
  • 3
  • 12
1

As I posted above in comments, you can use While for mutating collections. This is just to show concept that you can work with mutating collection without fear that item will not be found at specific pointer. But you will need to track items yourself.

// you will need some sort of indexer
int idx = 0;
while (coll.count > idx)
{

   if (condition met)
   {       
       coll.RemoveAt(idx);
       // you just removed an item, don't up the index
   }
   else
   {
      // Do something with item, for example, or add one more item
      idx += 1;
   }
}

May be not as efficient, but this way you're Ok with mutation of your collection

T.S.
  • 18,195
  • 11
  • 58
  • 78
0

This scenario will only happen if you have an active enumerator for the collection. The foreach loop uses such an enumerator. A for loop does not, you have to keep track of where in the collection you currently are, normally by using an index variable.

There is no general pattern to fix this. Personally, I changed my logic if I found my code was modifying the collection it was iterating over, but maybe your code is better suited to the for loop approach.

nvoigt
  • 75,013
  • 26
  • 93
  • 142
0

You are getting this exception because in the foreach loop you are trying to modify the collection you are enumerating. In the for loop you can modify the elements of the collection but you need to be very careful not to go into infinite loop situation.

nsgocev
  • 4,390
  • 28
  • 37
0

for each loop doesn't guarantee the order of sequence.
In this case if you are updating the same collection within for each and try to use it again you will get the collection modified error.

for loop guarantees the order of execution of you sequence. You can visualize at what instance you need to update your data within your for loop.
Concerned about Performance and memory:

 class Program
{
    static void Main(string[] args)
    {
        string[] test = { "one", "two", "three" }; // your string
        // make a new list of strings to contain all the strings + all the modifications
        List<string> changetest = new List<string>();
        // print out the initial array of strings
        foreach (string s in test)
            Console.WriteLine(s);
        Console.WriteLine();
        // now change the string if it starts with @
        foreach (string s in test)
            if (s.StartsWith("@"))
                changetest.Add(s.Insert(0, "*")); // insert a *
            else
                changetest.Add(s); // else just add
        // print out the modified list of strings
        foreach (string s in changetest)
            Console.WriteLine(s);

        Console.ReadKey();
    }
}

In the above example to update/modify the 'test' sequence you need to maintain one more collection to receive the update.Though scope of the List is local and elements are less you may encounter a situation where you will be dealing with huge/unknown collection.

for each inherently says you cant modify the collection.

If you want more info on performance visit following Link

Community
  • 1
  • 1
Srikanth
  • 980
  • 3
  • 16
  • 30
0

This is pretty much by design. You cannot modify a collection being iterated inside a foreach as explained in this MSDN Documentation

The foreach statement is used to iterate through the collection to get the information that you want, but can not be used to add or remove items from the source collection to avoid unpredictable side effects. If you need to add or remove items from the source collection, use a for loop.

Leo
  • 14,625
  • 2
  • 37
  • 55
0

The problem when you want to remove items from a list you're iterating over is that the enumerator loses track of where you are in the list.

It's a bit like sawing a branch on which you are sitting. You shoudn't do it because it will have unecptected behaviour.

Now, how can this be solved?

Quite easily, you need make a copy of your list, iterate over your copy, and remove the items from your original list.

Sounds like a lot of code, but the only thing that you need to do is add ToList() in your foreach statement.

Example:

// This will throw an error, because you're modifying the original collection in your foreach statement.
var persons = new List<Persons>();
persons.Add(new Person());
persons.Add(new Person());
persons.Add(new Person());

foreach (var p in persons) {
    persons.remove(p);
}

// This will NOT throw an error, because you're looping over a copy of the pesons list and remove items from the original one.
persons.Add(new Person());
persons.Add(new Person());
persons.Add(new Person());

foreach (var p in persons.ToList()) {
    persons.remove(p);
}

Now, why does this work?

It's because ToList() creates a copy of your list in memory. So basiclly, your iterating over a copy of the collection right now.

Complexity
  • 5,682
  • 6
  • 41
  • 84