2

根据标题 - C#/.NET 中是否有很好的内置选项可用于 IList 或 IDictionary 上的故障安全迭代?

我遇到问题的地方是类似于以下的代码:

IList<Foo> someList = new List<Foo>();

//...

foreach (Foo foo in someList) {
  if (foo.Bar) {
    someList.remove(foo);
  }
}

在第一次 foo.Bar 为真后抛出以下内容:

Type: System.InvalidOperationException
Message: Collection was modified; enumeration operation may not execute.

我知道简单的解决方法是这样做foreach (Foo foo in new List<Foo>(someList)),但是必须记住每次都这样做很烦人。单身的。时间。这出现了。

来自 Java 背景,这可以用 CopyOnWriteArrayList/ConcurrentHashMap 巧妙地处理(我知道使用这些列表还有其他惩罚。)C# 中是否有我不知道的等价物?

4

7 回答 7

2

如果您使用的是 .NET 4,则存在ConcurrentDictionary<TKey, TValue>and (以及同一命名空间ConcurrentBag<T>中的队列和堆栈)。据我所知,没有什么可以实现的。IList<T>

于 2010-10-26T19:53:17.560 回答
2

用 LINQ 来点乐趣怎么样。不会改变 List 的行为,但会使您的代码编写得更好。当然,这只适用于 List 而不是 IList 但仍然很酷。

someList.RemoveAll(Foo => Foo.Bar == true);
于 2010-10-26T20:04:17.367 回答
0

解决修改集合问题的一种简单方法是查询列表以查找要删除的项目并遍历这些项目的列表:

using System.Collections.Generic;
using System.Linq;

class Foo
{
    public bool Bar { get; set; }
}

class Program
{
    static void Main()
    {
        IList<Foo> someList = new List<Foo>() {
            new Foo() { Bar = true },
            new Foo() { Bar = false },
            new Foo() { Bar = true }
        };

        var itemsToRemove = someList.Where(f => f.Bar == true).ToArray();

        foreach (var foo in itemsToRemove)
        {
            someList.Remove(foo);
        }
    }
}
于 2010-10-26T19:58:23.333 回答
0

一个列表或字典可以在您对其进行迭代时处理任何更改,并确定哪些项目应该被迭代或不被迭代,这将是非常复杂的。最好为每种情况解决这个问题,因为大多数情况比想象的最糟糕的情况要简单得多。

例如,您可以这样做:

someList.Where(foo => foo.Bar).ToList().ForEach(foo => someList.remove(foo));

在大多数情况下,这比首先复制整个列表更有效,因为删除的项目通常要少得多。

于 2010-10-26T20:02:30.277 回答
0

您正在寻找的是一个健壮的迭代器。迭代器模式在 .NET 中通过IEnumerable<T>和实现IEnumerator<T>

默认情况下,您通过基类库获得的迭代器并不健壮。但是,您可以创建自己的。实现起来会有点棘手,因为您必须确保不会两次遍历相同的元素,并且不会跳过任何元素。但这当然是可能的。

如果创建派生自 的自定义类,List<T>则可以重写该GetEnumerator()方法以返回稳健的迭代器,从而使用foreach语法。

于 2010-10-26T20:07:49.070 回答
0

在 VB.net 中有一个名为 Collection 的类,它实现了一个 VB6 风格的集合。它在某些方面相当愚蠢(它始终是一个集合(字符串,对象)),但它的枚举器可以按照您的描述使用,并且它的性能相当快。它使用不区分大小写的字符串比较,我希望微软制作了一个通用版本,但它的枚举行为可能符合您的需求,我认为如果您导入正确的命名空间,应该可以在 C# 中使用它。

于 2010-10-26T20:20:24.553 回答
0

如果您只想删除东西,请使用提到的 RemoveAll 方法。如果你还想用一些元素做一些事情,你可以这样做:

for(int i = 0; i < list.Count; i++) { 
  var item = list[i];

  if(item.Foo) {
     list.RemoveAt(i--);
     continue;
  }

  item.Bar();
}
于 2013-06-01T00:42:35.137 回答