Collection was modified; enumeration operation may not execute in ArrayList

I'm trying to remove an item from an ArrayList and I get this Exception:
Collection was modified; enumeration operation may not execute.

Any ideas?

180900 次浏览

Don't modify the list inside of a loop which iterates through the list.

Instead, use a for() or while() with an index, going backwards through the list. (This will let you delete things without getting an invalid index.)

var foo = new List<Bar>();


for(int i = foo.Count-1; i >= 0; --i)
{
var item = foo[i];
// do something with item
}

You are removing the item during a foreach, yes? Simply, you can't. There are a few common options here:

  • use List<T> and RemoveAll with a predicate
  • iterate backwards by index, removing matching items

    for(int i = list.Count - 1; i >= 0; i--) {
    if({some test}) list.RemoveAt(i);
    }
    
  • use foreach, and put matching items into a second list; now enumerate the second list and remove those items from the first (if you see what I mean)

Instead of foreach(), use a for() loop with a numeric index.

One way is to add the item(s) to be deleted to a new list. Then go through and delete those items.

Here's an example (sorry for any typos)

var itemsToRemove = new ArrayList();  // should use generic List if you can


foreach (var item in originalArrayList) {
if (...) {
itemsToRemove.Add(item);
}
}


foreach (var item in itemsToRemove) {
originalArrayList.Remove(item);
}

OR if you're using 3.5, Linq makes the first bit easier:

itemsToRemove = originalArrayList
.Where(item => ...)
.ToArray();


foreach (var item in itemsToRemove) {
originalArrayList.Remove(item);
}

Replace "..." with your condition that determines if item should be removed.

I like to iterate backward using a for loop, but this can get tedious compared to foreach. One solution I like is to create an enumerator that traverses the list backward. You can implement this as an extension method on ArrayList or List<T>. The implementation for ArrayList is below.

    public static IEnumerable GetRemoveSafeEnumerator(this ArrayList list)
{
for (int i = list.Count - 1; i >= 0; i--)
{
// Reset the value of i if it is invalid.
// This occurs when more than one item
// is removed from the list during the enumeration.
if (i >= list.Count)
{
if (list.Count == 0)
yield break;


i = list.Count - 1;
}


yield return list[i];
}
}

The implementation for List<T> is similar.

    public static IEnumerable<T> GetRemoveSafeEnumerator<T>(this List<T> list)
{
for (int i = list.Count - 1; i >= 0; i--)
{
// Reset the value of i if it is invalid.
// This occurs when more than one item
// is removed from the list during the enumeration.
if (i >= list.Count)
{
if (list.Count == 0)
yield break;


i = list.Count - 1;
}


yield return list[i];
}
}

The example below uses the enumerator to remove all even integers from an ArrayList.

    ArrayList list = new ArrayList() {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};


foreach (int item in list.GetRemoveSafeEnumerator())
{
if (item % 2 == 0)
list.Remove(item);
}

I agree with several of the points I've read in this post and I've incorporated them into my solution to solve the exact same issue as the original posting.

That said, the comments I appreciated are:

  • "unless you are using .NET 1.0 or 1.1, use List<T> instead of ArrayList. "

  • "Also, add the item(s) to be deleted to a new list. Then go through and delete those items." .. in my case I just created a new List and the populated it with the valid data values.

e.g.

private List<string> managedLocationIDList = new List<string>();
string managedLocationIDs = ";1321;1235;;" // user input, should be semicolon seperated list of values


managedLocationIDList.AddRange(managedLocationIDs.Split(new char[] { ';' }));
List<string> checkLocationIDs = new List<string>();


// Remove any duplicate ID's and cleanup the string holding the list if ID's
Functions helper = new Functions();
checkLocationIDs = helper.ParseList(managedLocationIDList);


...
public List<string> ParseList(List<string> checkList)
{
List<string> verifiedList = new List<string>();


foreach (string listItem in checkList)
if (!verifiedList.Contains(listItem.Trim()) && listItem != string.Empty)
verifiedList.Add(listItem.Trim());


verifiedList.Sort();
return verifiedList;
}

Am I missing something? Somebody correct me if I'm wrong.

list.RemoveAll(s => s.Name == "Fred");

using ArrayList also you can try like this

ArrayList arraylist = ... // myobject data list


ArrayList temp = (ArrayList)arraylist.Clone();


foreach (var item in temp)
{
if (...)
arraylist.Remove(item);
}