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

San*_*eep 4 .net c# 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++;
        }
Run Code Online (Sandbox Code Playgroud)

但是当我将列表更改为list.toarray有效时.

Jar*_*Par 7

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

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

  1. list
  2. 创建的数组

你实际上然后枚举数组,因此修改原始list就好了.


sup*_*cat 5

Microsoft指定修改该对象时,任何实现iEnumerable的对象都必须使所有现有枚举无效。此要求的一般原因是,对于许多类型的集合,很难确保在修改集合时枚举器将表现出合理的行为。例如,假设一个List包含五个值(A,B,C,D,E),并且一个枚举数通过将n设置为元素数来工作,然后输出element(0),element(1)等。元素(n-1)。如果在枚举器枚举element(2)[C]时,元素BB插入元素(1)[即B]之后,则枚举器可能会继续输出元素(3)[再次为C],然后元素4 [D],然后决定完成,因为它输出了所有五个元素。那将是一个糟糕的情况(一个要素出现两次,而一个要素丢失)。

如果对集合进行某种修改以防止枚举器产生有意义的结果,则使枚举器无效应该是合理的。但是,在我看来,即使集合经过修改,但能够满足以下合同的枚举数也应这样做,而不是抛出异常:

  1. 在枚举期间存在的任何项目都必须准确返回一次。
  2. 在枚举持续时间内存在的任何项目,必须只返回一次或完全不返回,但不要求返回哪些项目(如果有)。
  3. 返回项目的顺序必须是枚举器的有效序列(例如,SortedList必须按已排序的顺序返回项目;如果将某个项目添加到列表中,且该列表早于已输出的项目,则不得枚举该项目)。
  4. 出于(1)和(2)的目的,即使键是相同的,删除的一项也应视为与添加的项有所区别;如果更改了项的键,则更改前的项与更改后的项被认为是不同的。
  5. 不能保证通过Reset进行的重复枚举返回相同的项目。

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