【问题标题】:Are there good IList and IDictionary implementations in C# that support fail-safe iteration?C# 中是否有良好的 IList 和 IDictionary 实现支持故障安全迭代?
【发布时间】:2010-10-26 19:49:36
【问题描述】:

根据标题 - 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&lt;Foo&gt;(someList)),但每次都必须记住这样做很烦人。单身的。时间。这出现了。

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

【问题讨论】:

  • 对于这种特殊情况,List&lt;T&gt;(但不是IList&lt;T&gt;)有一个RemoveAll(Predicate&lt;T&gt; condition) 方法。

标签: c# collections iteration concurrent-collections


【解决方案1】:

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

【讨论】:

  • 遗憾的是,我们现在只使用 .NET 3.5 - 但这正是我想要的。谢谢!
【解决方案2】:

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

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

【讨论】:

  • 吹毛求疵:RemoveAll 与 LINQ 无关。它只是List&lt;T&gt; 类本身的一个实例方法。
  • +1 - 很好的答案,我需要尝试更频繁地使用该语法。不过,在这种情况下,我的 someList.remove() 方法实际上是迭代下面的几个方法调用,所以我不能做那么简单的事情。
【解决方案3】:

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

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);
        }
    }
}

【讨论】:

【解决方案4】:

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

例如,您可以这样做:

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

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

【讨论】:

  • 处理任意突变会很困难。但是,如果有一个标准的IEditableEnumerableCollection,其枚举数包括AddAddBeforeAddAfterReplaceRemove 方法,那么许多类型的集合都可以轻松实现它。请注意,使用这样的枚举器插入或删除列表中的多个项目(使用AddBeforeAddAfterremove)可能比使用多个离散操作更快,因为插入/删除可以推迟到枚举完成完成后,可以将它们合并到列表中。
【解决方案5】:

您正在寻找的是一个强大的迭代器。迭代器模式在 .NET 中通过 IEnumerable&lt;T&gt;IEnumerator&lt;T&gt; 实现。

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

如果您创建一个派生自List&lt;T&gt; 的自定义类,您可以覆盖GetEnumerator() 方法以返回强大的迭代器,因此使用foreach 语法。

【讨论】:

    【解决方案6】:

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

    【讨论】:

      【解决方案7】:

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

      for(int i = 0; i < list.Count; i++) { 
        var item = list[i];
      
        if(item.Foo) {
           list.RemoveAt(i--);
           continue;
        }
      
        item.Bar();
      }
      

      【讨论】:

        猜你喜欢
        • 2013-06-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-12-17
        • 2016-11-15
        相关资源
        最近更新 更多