枚举时项目更改是否会影响枚举?

时间:2009-07-11 21:55:14

标签: c# .net concurrency ienumerable enumeration

想象一下

foreach(var item in enumerable)

可枚举项目发生变化。它会影响当前的foreach吗?

示例:

var enumerable = new List<int>();
enumerable.Add(1);
Parallel.ForEach<int>(enumerable, item =>
{ 
     enumerable.Add(item + 1);
});

它会永远循环吗?

4 个答案:

答案 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类都会在更改集合时抛出异常,如果它们不符合以下条件:

  1. 在整个枚举过程中未经修改的任何项目将只返回一次。
  2. 在枚举期间添加或删除的项目最多应返回一次,但如果在枚举期间删除并重新添加对象,则每次重新添加可视为创建新“项目” 。
  3. 如果集合保证以排序顺序返回内容,即使插入和删除项目,也必须满足该保证[例如,如果将“Fred”添加到按字母顺序排序的列表中,并且已经枚举了“George”,则在该枚举期间不得出现“Fred”。

如果有一些方法可以通过哪些方法报告集合是否能够满足上述标准(不抛出异常),即使在修改时也是如此,因为它们在尝试例如时非常有用。删除符合特定标准的所有项目。

相关问题