C# 中是否有支持故障安全迭代的良好 IList 和 IDictionary 实现?
根据标题 - C#/.NET 中是否有良好的内置选项可用于 IList 或 IDictionary 上的故障安全迭代?
我遇到问题的地方是类似于以下代码:
IList<Foo> someList = new List<Foo>();
//...
foreach (Foo foo in someList) {
if (foo.Bar) {
someList.remove(foo);
}
}
在第一次 foo.Bar 为 true 后抛出以下内容:
Type: System.InvalidOperationException
Message: Collection was modified; enumeration operation may not execute.
我知道简单的解决方法是执行 foreach (Foo foo in new List
,但是必须记住每次都这样做很烦人。单身的。时间。这出现了。
来自 Java 背景,这可以通过 CopyOnWriteArrayList/ConcurrentHashMap 巧妙地处理(我知道使用这些列表还会带来其他惩罚。)C# 中是否有我不知道的等效项?
Per the title - are there good built-in options in C#/.NET for fail-safe iteration over an IList or an IDictionary?
Where I'm running into problems is with code similar to the following:
IList<Foo> someList = new List<Foo>();
//...
foreach (Foo foo in someList) {
if (foo.Bar) {
someList.remove(foo);
}
}
which throws the following after the first time foo.Bar is true:
Type: System.InvalidOperationException
Message: Collection was modified; enumeration operation may not execute.
I know the easy workaround is to do foreach (Foo foo in new List<Foo>(someList))
, but it's annoying to have to remember to do that every. single. time. this comes up.
Coming from a Java background, that's something that can be handled neatly with a CopyOnWriteArrayList/ConcurrentHashMap (I'm aware that there are other penalties associated with using those lists.) Is there an equivalent in C# that I'm just not aware of?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
如果您使用的是 .NET 4,则有
ConcurrentDictionary
和ConcurrentBag
(以及 同一命名空间)。据我所知,没有任何东西可以实现IList
。If you're using .NET 4, there's
ConcurrentDictionary<TKey, TValue>
andConcurrentBag<T>
(and a queue and a stack in the same namespace). There's nothing which implementsIList<T>
as far as I'm aware though.来点 LINQ 的乐趣怎么样?不会改变 List 的行为,但会使代码编写得更好。当然,这只适用于 List,不适用于 IList,但仍然很酷。
How about some fun with LINQ. Doesn't change behavior of List but makes writing your code nicer. Of course this only works on List not IList but still cool.
解决修改集合问题的一个简单方法是查询列表以查找要删除的项目并迭代这些项目的列表:
A simple way to resolve your issue with modifying the collection would be to query the list for the items to remove and the iterate over the list of these items:
一个列表或字典可以在迭代时处理任何更改,并确定哪些项目应该迭代或不迭代,这将非常复杂。最好针对每种情况都解决这个问题,因为大多数情况比想象的最糟糕的情况要简单得多。
例如,您可以这样做:
在大多数情况下,这比首先复制整个列表更有效,因为删除的项目通常要少得多。
A list or dictionary that could handle any change while you are iterating over it, and figure out which items should be iterated or not, would be very complicated. It's better to solve that for each situation, as most situations are much simpler than the worst imaginable.
You can for example do like this:
In most cases that is more efficent than first copying the entire list, as the removed items usually are considerably fewer.
您正在寻找的称为鲁棒迭代器。迭代器模式在 .NET 中通过
IEnumerable
和IEnumerator
实现。默认情况下,通过基类库获得的迭代器并不健壮。但是,您可以创建自己的。实现起来会有点棘手,因为您必须确保不会两次遍历同一个元素,并且不会跳过任何元素。但这当然是可能的。
如果您创建派生自
List
的自定义类,则可以重写GetEnumerator()
方法以返回稳健迭代器,因此使用foreach
语法。What you're looking for is called a robust iterator. The iterator pattern is implemented in .NET via
IEnumerable<T>
andIEnumerator<T>
.By default the iterators that you get with the Base Class Library are not robust. But, you can create your own. It will be a little tricky to implement because you have to make sure that you don't go over the same element twice, and you don't skip any elements. But it is certainly possible.
If you create a custom class that derives from
List<T>
, you can override theGetEnumerator()
method to return the robust iterator and therefore use theforeach
syntax.在VB.net中有一个名为Collection的类,它实现了VB6风格的集合。它在某些方面相当愚蠢(它始终是一个集合(String,Object)),但它的枚举器可以按照您的描述使用,并且它的性能相当快。它使用不区分大小写的字符串比较,我希望 Microsoft 制作了一个通用版本,但它的枚举行为可能适合您的需求,我认为如果导入正确的命名空间,应该可以在 C# 中使用它。
In VB.net there is an class called Collection which implements a VB6-style collection. It's rather goofy in some ways (it's always a collection(Of String, Object)) but its enumerator can be used as you describe, and its performance is reasonably fast. It uses non-case-sensitive string comparisons, and I wish Microsoft had made a generic version, but its enumeration behavior might fit your needs and I think it should be possible to use it in C# if you import the right namespace.
如果您只想删除内容,请使用提到的RemoveAll 方法。如果您还想对某些元素做一些事情,您可以这样做:
If you only want to remove stuff use the mentioned RemoveAll-method. If you also want to do stuff with some of the elements you can do this: