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

发布于 2024-08-31 12:13:37 字数 347 浏览 4 评论 0原文

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

Are there any data structures in the C# Collections library where modification of the structure does not invalidate iterators?

Consider the following:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

清浅ˋ旧时光 2024-09-07 12:13:37

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

请注意,对于此命名空间中的某些集合(例如,ConcurrentQueue),这通过仅在相关集合的“快照”上公开枚举器来实现。

来自 关于 ConcurrentQueue的 MSDN 文档

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

但并非所有集合都是如此。例如,ConcurrentDictionary 为您提供了一个枚举器,用于在调用 MoveNext 之间维护底层集合的更新。

来自 关于 ConcurrentDictionary

从返回的枚举器
字典可以安全地同时使用
与读取和写入
字典,但它没有
代表某个时刻的快照
字典。曝光的内容
通过枚举器可能包含
对词典进行的修改
调用 GetEnumerator 后。

如果您没有 4.0,那么我认为其他人是对的,.NET 没有提供这样的集合。不过,您始终可以通过执行 ConcurrentQueue 所做的相同操作(迭代快照)来构建自己的队列。

Yes, take a look at the System.Collections.Concurrent namespace in .NET 4.0.

Note that for some of the collections in this namespace (e.g., ConcurrentQueue<T>), this works by only exposing an enumerator on a "snapshot" of the collection in question.

From the MSDN documentation on ConcurrentQueue<T>:

The enumeration represents a
moment-in-time snapshot of the
contents of the queue. It does not
reflect any updates to the collection
after GetEnumerator was called. The
enumerator is safe to use concurrently
with reads from and writes to the
queue.

This is not the case for all of the collections, though. ConcurrentDictionary<TKey, TValue>, for instance, gives you an enumerator that maintains updates to the underlying collection between calls to MoveNext.

From the MSDN documentation on ConcurrentDictionary<TKey, TValue>:

The enumerator returned from the
dictionary is safe to use concurrently
with reads and writes to the
dictionary, however it does not
represent a moment-in-time snapshot of
the dictionary. The contents exposed
through the enumerator may contain
modifications made to the dictionary
after GetEnumerator was called.

If you don't have 4.0, then I think the others are right and there is no such collection provided by .NET. You can always build your own, however, by doing the same thing ConcurrentQueue<T> does (iterate over a snapshot).

行至春深 2024-09-07 12:13:37

根据这篇关于 IEnumerator 的 MSDN 文章,失效行为您发现 IEnumerable 的所有实现都需要它。

只要集合保持不变,枚举器就保持有效。如果
对集合进行更改,例如添加、修改或删除
元素,枚举器将不可恢复地失效,并且下一次调用
MoveNext 或 Reset 会引发 InvalidOperationException。如果集合是
在 MoveNext 和 Current 之间修改,Current 返回它所在的元素
设置为,即使枚举器已经失效。

According to this MSDN article on IEnumerator the invalidation behaviour you have found is required by all implementations of IEnumerable.

An enumerator remains valid as long as the collection remains unchanged. If
changes are made to the collection, such as adding, modifying, or deleting
elements, the enumerator is irrecoverably invalidated and the next call to
MoveNext or Reset throws an InvalidOperationException. If the collection is
modified between MoveNext and Current, Current returns the element that it is
set to, even if the enumerator is already invalidated.

闻呓 2024-09-07 12:13:37

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

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

您可以轻松实现这样的集合,也可以使用 FSharp.Core.dll 中的 FSharpList(尽管不是 .NET 4.0 的标准部分)

open Microsoft.FSharp.Collections;

// Create immutable list from other collection
var list = ListModule.OfSeq(anyCollection);
// now we can use `GetEnumerable`
var en = list.GetEnumerable();

// To modify the collection, you create a new collection that adds 
// element to the front (without actually copying everything)
var added = new FSharpList<int>(42, list);

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

Supporting this behavior requires some pretty complex internal handling, so most of the collections don't support this (I'm not sure about the Concurrent namespace).

However, you can very well simulate this behavior using immutable collections. They don't allow you to modify the collection by design, but you can work with them in a slightly different way and this kind of processing allows you to use enumerator concurrently without complex handling (implemented in Concurrent collections).

You can implement a collection like that easily, or you can use FSharpList<T> from FSharp.Core.dll (not a standard part of .NET 4.0 though):

open Microsoft.FSharp.Collections;

// Create immutable list from other collection
var list = ListModule.OfSeq(anyCollection);
// now we can use `GetEnumerable`
var en = list.GetEnumerable();

// To modify the collection, you create a new collection that adds 
// element to the front (without actually copying everything)
var added = new FSharpList<int>(42, list);

The benefit of immutable collections is that you can work with them (by creating copies) without affecting the original one, and so the behavior you wanted is "for free". For more information, there is a great series by Eric Lippert.

聆听风音 2024-09-07 12:13:37

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

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

The only way to do this is to make a copy of the list before you iterate it:

var myIter = new List<int>(myList).GetEnumerator();
め可乐爱微笑 2024-09-07 12:13:37

不,它们不存在。当结构发生变化时,所有 C# 标准集合都会使分子失效。

No, they do not exist. ALl C# standard collections invalidate the numerator when the structure changes.

唱一曲作罢 2024-09-07 12:13:37

使用for循环代替foreach,然后就可以修改它了。但我不会建议这样做......

Use a for loop instead of a foreach, and then you can modify it. I wouldn't advise it though....

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文