Есть ли хорошие реализации IList и IDictionary в C #, которые поддерживают отказоустойчивую итерацию? - PullRequest
2 голосов
/ 26 октября 2010

По названию - есть ли хорошие встроенные опции в 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 ]

2 голосов
/ 27 октября 2010

Как насчет веселья с LINQ. Не меняет поведение List, но делает написание вашего кода приятнее. Конечно, это работает только в List, но не в IList, но все равно здорово.

someList.RemoveAll(Foo => Foo.Bar == true);
2 голосов
/ 26 октября 2010

Если вы используете .NET 4, есть ConcurrentDictionary<TKey, TValue> и ConcurrentBag<T> (и очередь и стек в том же пространстве имен ),Там нет ничего, что реализует IList<T>, насколько мне известно.

0 голосов
/ 01 июня 2013

Если вы хотите только удалить материал, используйте упомянутый метод RemoveAll. Если вы также хотите сделать что-то с некоторыми элементами, вы можете сделать это:

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

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

  item.Bar();
}
0 голосов
/ 27 октября 2010

В VB.net есть класс Collection, который реализует коллекцию в стиле VB6. В некотором смысле это довольно глупо (это всегда коллекция (Of String, Object)), но его перечислитель можно использовать, как вы описываете, и его производительность достаточно высокая. Он использует сравнения строк без учета регистра, и я хотел бы, чтобы Microsoft сделала универсальную версию, но ее поведение перечисления могло бы соответствовать вашим потребностям, и я думаю, что можно использовать ее в C #, если вы импортируете правильное пространство имен.

0 голосов
/ 27 октября 2010

То, что вы ищете, называется надежный итератор .Шаблон итератора реализован в .NET через IEnumerable<T> и IEnumerator<T>.

. По умолчанию итераторы, которые вы получаете с библиотекой базовых классов, не являются надежными.Но вы можете создать свой собственный.Это будет немного сложно реализовать, потому что вы должны убедиться, что вы не пропускаете один и тот же элемент дважды и не пропускаете никаких элементов.Но это, безусловно, возможно.

Если вы создаете пользовательский класс, производный от List<T>, вы можете переопределить метод GetEnumerator() для возврата робастного итератора и, следовательно, использовать foreach синтаксис.

0 голосов
/ 27 октября 2010

Список или словарь, который может обрабатывать любые изменения во время итерации по нему и определять, какие элементы следует повторять или нет, будет очень сложным. Лучше решить это для каждой ситуации, так как большинство ситуаций намного проще, чем худшие из возможных.

Например, вы можете сделать так:

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

В большинстве случаев это более эффективно, чем первое копирование всего списка, поскольку удаленных элементов обычно значительно меньше.

0 голосов
/ 26 октября 2010

Простой способ решить вашу проблему с изменением коллекции - запросить список элементов для удаления и выполнить перебор списка этих элементов:

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