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
答案 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,然后您可以修改它。我不会建议它......