这个问题很多时候都在这个论坛上被问到。我知道问题的解决方案。但我很想知道为什么“修改集合时无法执行枚举操作”
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
时,它可行。
答案 0 :(得分:6)
通常,.Net集合不支持同时枚举和修改。当您在枚举它时,行list[i] = "test"
会修改集合list
,因此会生成异常。确实,这是一个微不足道的修改,并不会影响列表的结构,但List<T>
甚至将修改视为具有破坏性。
ToArray
版本有效,因为现在您有2个集合
list
您实际上是在枚举数组,因此修改原始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],然后决定它完成,因为它输出所有五个元素。这将是一个糟糕的情况(一个元素出现两次,一个失踪)。
如果以一种阻止枚举器产生合理结果的方式修改集合,则枚举器应该失效,这当然是合理的。但是,在我看来,能够满足以下合同的调查员应该这样做,即使修改了集合,也不应该抛出异常:
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++;
}
请注意,在第二种情况下,您不是要枚举您正在修改的集合,而是枚举一个全新的集合。