8

想象一下,在一个

foreach(var item in enumerable)

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

例子:

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

它会永远循环吗?

4

4 回答 4

19

一般来说,它应该抛出一个异常。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._version操作进行修改(递增)。

于 2009-07-11T22:08:33.640 回答
5

取决于枚举器的性质。他们中的许多人在集合更改时抛出异常。

例如,如果集合在枚举期间发生更改,则List<T>抛出一个。InvalidOperationException

于 2009-07-11T22:01:53.577 回答
2

Microsoft 的IEnumerable[and IEnumerable<T>--non-generic names will refer to both] 文档建议,任何时候更改实现这些接口的对象时,它都应该使之前生成的IEnumerator[ ] 的任何实例无效,从而导致它们在未来的访问尝试IEnumerator<T>中抛出InvalidOperationException. 尽管 Microsoft 的文档中没有任何文件记录了这种立场的任何变化,但他们的实际实现IEnumerable似乎遵循更宽松的规则,即IEnumerator如果底层集合被修改,则不应表现得毫无意义;如果它不能“明智地”表现,它应该抛出InvalidOperationException . 不幸的是,由于该规则没有明确说明,而是从他们的类的行为中推断出来的,所以不清楚“明智的”行为到底应该意味着什么。

我所知道的所有 Microsoft 类如果不能满足以下条件,则会在更改集合时抛出异常:

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

如果集合可以通过某种方式报告它们是否可以满足上述标准(不抛出异常),即使在修改时也会有所帮助,因为它们在尝试删除所有满足特定标准的项目时非常有用。

于 2013-01-27T17:46:40.213 回答
1

这完全取决于 IEnumerable 是如何实现的。

使用 List,它会抛出 IllegalOperationException。但是不要依赖 IEnumarables 的这种行为。有些循环无穷无尽,会很快抛出 OutOfMemory 异常。

于 2009-07-11T22:01:32.267 回答