关于c#:集合被修改;

Collection was modified; enumeration operation may not execute in ArrayList

本问题已经有最佳答案,请猛点这里访问。

我试图从ArrayList中删除一个项目,我得到了这个例外:Collection was modified; enumeration operation may not execute.

有什么想法吗?


你是在foreach中移除物品,是吗?简单地说,你不能。这里有几个常见的选项:

  • ListRemoveAll与谓词一起使用
  • 按索引向后迭代,删除匹配项

    1
    2
    3
    for(int i = list.Count - 1; i >= 0; i--) {
        if({some test}) list.RemoveAt(i);
    }
  • 使用foreach,将匹配项放入第二个列表;现在枚举第二个列表并从第一个列表中删除这些项(如果您明白我的意思的话)


这是一个例子(抱歉有错别字)

1
2
3
4
5
6
7
8
9
10
11
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);
}

或者,如果您使用的是3.5,那么Linq会使第一点更容易:

1
2
3
4
5
6
7
itemsToRemove = originalArrayList
  .Where(item => ...)
  .ToArray();

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

将"…"替换为确定是否应删除项的条件。


我喜欢使用for循环向后迭代,但与foreach相比,这可能会变得单调乏味。我喜欢的一个解决方案是创建一个反向遍历列表的枚举器。您可以在ArrayListList上作为扩展方法来实现此功能。ArrayList的实现如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    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];
        }
    }

List的实现是相似的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    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];
        }
    }

下面的示例使用枚举器从ArrayList中删除所有偶数整数。

1
2
3
4
5
6
7
    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);
    }

一种方法是将要删除的项添加到新列表中。然后浏览并删除这些项目。


不要在循环中修改列表,循环将遍历列表。

相反,使用带索引的for()while(),向后浏览列表。(这将允许您在不获取无效索引的情况下删除内容。)

1
2
3
4
5
6
7
var foo = new List<Bar>();

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


我错过什么了吗?如果我错了,有人纠正我。

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


我同意我在这篇文章中读到的几个观点,并且我已经将它们融入到我的解决方案中,以解决与原始文章完全相同的问题。

也就是说,我欣赏的评论是:

  • "除非您使用.NET 1.0或1.1,否则请使用List而不是ArrayList。"

  • "同时,将要删除的项目添加到新列表中。然后检查并删除这些项目。"…在我的例子中,我刚刚创建了一个新列表,并用有效的数据值填充它。

例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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;
}

使用带数字索引的for()循环,而不是foreach()。


使用ArrayList也可以这样尝试

1
2
3
4
5
6
7
8
9
ArrayList arraylist = ... // myobject data list

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

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