在C#中是否有支持故障安全迭代的良好IList和IDictionary实现?

时间:2010-10-26 19:49:37

标签: c# collections iteration concurrent-collections

根据标题 - 在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#中有一个等价物我不是知道吗?

7 个答案:

答案 0 :(得分:2)

如果您使用的是.NET 4,则ConcurrentDictionary<TKey, TValue>ConcurrentBag<T>(以及same namespace中的队列和堆栈)。据我所知,没有什么可以实现IList<T>

答案 1 :(得分:2)

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

someList.RemoveAll(Foo => Foo.Bar == true);

答案 2 :(得分: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);
        }
    }
}

答案 3 :(得分:0)

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

你可以这样做:

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

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

答案 4 :(得分:0)

您正在寻找的是强大的迭代器。迭代器模式在.NET中通过IEnumerable<T>IEnumerator<T>实现。

默认情况下,使用基类库获得的迭代器不健壮。但是,你可以创建自己的。实现起来有点棘手,因为你必须确保不要重复两次相同的元素,并且不要跳过任何元素。但它肯定是可能的。

如果创建派生自List<T>的自定义类,则可以覆盖GetEnumerator()方法以返回 robust iterator ,因此使用foreach语法

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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

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

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

  item.Bar();
}