收集被修改;枚举操作可能无法执行

时间:2010-10-06 17:09:48

标签: c# .net collections

这个问题很多时候都在这个论坛上被问到。我知道问题的解决方案。但我很想知道为什么“修改集合时无法执行枚举操作”

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

        list.Add("a");

        list.Add("b");

        int[] array = new int[6] { 1, 2, 3, 4, 5, 5 };

        HashSet<int> hashSet = new HashSet<int>();

        int i = 0;

        foreach (string s in list)
        {
            list[i] = "test";

            i++;
        }

但当我将列表更改为list.toarray时,它可行。

3 个答案:

答案 0 :(得分:6)

通常,.Net集合不支持同时枚举和修改。当您在枚举它时,行list[i] = "test"会修改集合list,因此会生成异常。确实,这是一个微不足道的修改,并不会影响列表的结构,但List<T>甚至将修改视为具有破坏性。

ToArray版本有效,因为现在您有2个集合

  1. list
  2. 创建的数组
  3. 您实际上是在枚举数组,因此修改原始list就好了。

答案 1 :(得分:5)

Microsoft指定实现iEnumerable的任何对象必须在修改对象时使任何现有枚举无效。这一要求的一般原因是,对于许多类型的集合,很难确保枚举器在修改集合时表现得明智。例如,假设List包含五个值(A,B,C,D,E),枚举器通过将n设置为元素数,然后输出元素(0),元素(1)等直到元件(N-1)。如果,当枚举器枚举元素(2)[C]时,元素BB插入元素(1)之后[即B],然后枚举器可以继续输出元素(3)[再次是C],然后是元素4 [D],然后决定它完成,因为它输出所有五个元素。这将是一个糟糕的情况(一个元素出现两次,一个失踪)。

如果以一种阻止枚举器产生合理结果的方式修改集合,则枚举器应该失效,这当然是合理的。但是,在我看来,能够满足以下合同的调查员应该这样做,即使修改了集合,也不应该抛出异常:

  1. 在整个枚举期间存在的任何项目必须只返回一次。
  2. 在枚举期间的部分时间内存在的任何项目必须只返回一次或根本不返回,但不要求返回哪些项目(如果有的话)。
  3. 返回项目的顺序必须是枚举器的有效序列(例如,SortedList必须按排序顺序返回项目;如果项目被添加到列表中,该列表将在已输出项目之前,不得枚举该项目)。
  4. 为了(1)和(2)的目的,即使密钥相同,被删除的项目也应视为与添加的项目不同;如果项目的键被更改,则更改前的项目被视为与之后的项目不同。
  5. 通过重置重复枚举不保​​证返回相同的项目。

VB6风格的集合似乎符合上述语义,但其他标准集合都没有。太糟糕了 - 某些数据结构可以很好地满足这些要求,但足以避免在当前规则下经常需要的列表数据重复。

答案 2 :(得分:1)

当您在foreach中执行list.ToArray()时,您正在制作列表内容的完整副本,并在副本上枚举。在枚举列表时,您不再对列表进行更改。

这样做:

foreach (string s in list.ToArray())
{
   list[i] = "test";
   i++;
}

实际上与执行此操作相同:

string[] newArray = list.ToArray();
foreach (string s in newArray)
{
   list[i] = "test";
   i++;
}

请注意,在第二种情况下,您不是要枚举您正在修改的集合,而是枚举一个全新的集合。