是否有任何C#集合,其中修改不会使迭代器失效?

时间:2010-05-02 14:16:13

标签: c# .net collections iterator

C#Collections库中是否有任何数据结构,其中结构的修改不会使迭代器失效?

请考虑以下事项:

List<int> myList = new List<int>();
myList.Add( 1 );
myList.Add( 2 );
List<int>.Enumerator myIter = myList.GetEnumerator();
myIter.MoveNext();  // myIter.Current == 1
myList.Add( 3 );
myIter.MoveNext();  // throws InvalidOperationException

6 个答案:

答案 0 :(得分:11)

是的,请查看.NET 4.0中的System.Collections.Concurrent命名空间。

请注意,对于此命名空间中的某些集合(例如,ConcurrentQueue<T>),这只会在相关集合的“快照”上公开枚举器。

来自the MSDN documentation on ConcurrentQueue<T>

  

枚举代表一个   即时快照   队列的内容。它不是   反映集合的任何更新   在调用GetEnumerator之后。该   枚举器可以安全地同时使用   读取和写入   队列中。

但并非所有馆藏都是如此。例如,ConcurrentDictionary<TKey, TValue>为您提供了一个枚举器,用于在调用MoveNext之间维护对基础集合的更新。

来自the MSDN documentation on ConcurrentDictionary<TKey, TValue>

  

从中返回的枚举器   字典可以安全地同时使用   读取和写入   字典,但它没有   代表一个时刻的快照   词典。内容暴露   通过调查员可能包含   对字典做出的修改   在调用GetEnumerator之后。

如果没有 4.0,那么我认为其他的是正确的,并且.NET没有提供此类集合。但是,您可以通过执行ConcurrentQueue<T>所做的相同操作(迭代快照)来构建自己的。

答案 1 :(得分:8)

根据this MSDN article on IEnumerator,IEnumerable的所有实现都需要您找到的失效行为。

  

只要集合保持不变,枚举器仍然有效。如果   对集合进行更改,例如添加,修改或删除   元素,枚举器无法恢复无效和下一次调用   MoveNext或Reset会抛出InvalidOperationException。如果收藏是   在MoveNext和Current之间修改,Current返回它所在的元素   设置为,即使枚举器已经失效。

答案 2 :(得分:5)

支持这种行为需要一些相当复杂的内部处理,因此大多数集合都不支持这种情况(我不确定Concurrent命名空间)。

但是,您可以使用不可变集合很好地模拟此行为。它们不允许您按设计修改集合,但您可以稍微不同的方式使用它们,这种处理允许您同时使用枚举器而无需复杂处理(在{{中实现) 1}}集合)。

您可以轻松地实现这样的集合,或者您可以使用Concurrent中的FSharpList<T>(不是.NET 4.0的标准部分):

FSharp.Core.dll

不可变集合的好处是您可以使用它们(通过创建副本)而不影响原始集合,因此您想要的行为是“免费”。有关详细信息,请参阅great series by Eric Lippert

答案 3 :(得分:1)

执行此操作的唯一方法是在迭代之前复制列表:

var myIter = new List<int>(myList).GetEnumerator();

答案 4 :(得分:0)

不,他们不存在。 ALl C#标准集合在结构更改时使分子无效。

答案 5 :(得分:-1)

使用for循环而不是foreach,然后您可以修改它。我不会建议它......