想象一下
foreach(var item in enumerable)
可枚举项目发生变化。它会影响当前的foreach吗?
示例:
var enumerable = new List<int>();
enumerable.Add(1);
Parallel.ForEach<int>(enumerable, item =>
{
enumerable.Add(item + 1);
});
它会永远循环吗?
答案 0 :(得分:15)
通常,它应该抛出异常。
GetEnumerator()的List<T>
实现提供Enumerator<T>
对象,其MoveNext()
方法如下所示(来自Reflector):
public bool MoveNext()
{
List<T> list = this.list;
if ((this.version == list._version) && (this.index < list._size))
{
this.current = list._items[this.index];
this.index++;
return true;
}
return this.MoveNextRare();
}
private bool MoveNextRare()
{
if (this.version != this.list._version)
{
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
}
this.index = this.list._size + 1;
this.current = default(T);
return false;
}
对每个修改List的操作修改(递增)list._version
。
答案 1 :(得分:5)
取决于调查员的性质。当集合发生变化时,许多人会抛出异常。
例如,如果在枚举期间集合发生更改,List<T>
将抛出InvalidOperationException
。
答案 2 :(得分:1)
这完全取决于IEnumerable的实现方式。
使用List,它将抛出IllegalOperationException。但是不要依赖于IEnumarables的这种行为。有些循环无穷无尽,会快速抛出OutOfMemoryexception。
答案 3 :(得分:1)
Microsoft的IEnumerable
[和IEnumerable<T>
- 非通用名称的文档将引用两者]建议在实现这些接口的对象发生更改时,它应该使{{1}的任何实例无效它之前生成的[IEnumerator
]导致它们在将来的访问尝试中抛出IEnumerator<T>
。虽然Microsoft的文档中没有任何内容记录了这种立场的任何变化,但它们InvalidOperationException
的实际实现似乎遵循更宽松的规则,即如果底层集合被修改,IEnumerable
不应该无意义地表现;它应该抛出IEnumerator
如果它不能表现得“理智”。不幸的是,由于该规则没有明确说明,而是从其班级的行为推断出来,因此不清楚究竟“合理”行为应该是什么意思。
我知道的所有Microsoft类都会在更改集合时抛出异常,如果它们不符合以下条件:
如果有一些方法可以通过哪些方法报告集合是否能够满足上述标准(不抛出异常),即使在修改时也是如此,因为它们在尝试例如时非常有用。删除符合特定标准的所有项目。