Как удалить объекты из коллекции Enumerable в цикле [дубликат]

StackOverflow https://stackoverflow.com/questions/404557

Вопрос

Дублировать

Изменение коллекции при ее переборе


Есть ли у кого-нибудь хороший шаблон, позволяющий мне обойти невозможность удалять объекты во время цикла через перечислимую коллекцию (например, IList или KeyValuePairs в словаре)

Например, следующее не удается, поскольку оно изменяет список, перечисляемый во время foreach.

foreach (MyObject myObject in MyListOfMyObjects)
{
     if (condition) MyListOfMyObjects.Remove(myObject);
}

Раньше я использовал два метода.

Я заменил foreach обратным циклом for (чтобы не менять индексы, которые я просматриваю, если удаляю объект).

Я также попытался сохранить новую коллекцию объектов для удаления внутри цикла, затем просмотрел эту коллекцию и удалил объекты из исходной коллекции.

Они работают нормально, но ни чувствует здорово, и мне интересно, придумал ли кто-нибудь более элегантный решение проблемы

Это было полезно?

Решение

есть полезная вещь List<T>.RemoveAll(Predicate<T> match) метод, который, я думаю, предназначен для этого: http://msdn.microsoft.com/en-us/library/wdka673a.aspx

Другие советы

Это довольно просто, но когда я планирую удалять элементы из IEnumerable/IList, я обычно просто делаю копию:

foreach (MyObject myObject in new List<MyObject>(MyListOfMyObjects))
{
     if (condition) MyListOfMyObjects.Remove(myObject);
}

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

Сделайте обратное, создав новый список:

List myFilteredList = new List();
foreach (MyObject myObject in myListOfMyObjects)
{
     if (!condition) myFilteredList.Add(myObject);
}

Затем используйте новый список там, где он вам нужен.

Вы также можете легко использовать выражение LINQ, опять же, инвертируя условие.Это имеет дополнительное преимущество: не создается новая структура, но есть и подводные камни, связанные с ленивым перечислением:

var myFilteredList = from myObject in myListOfMyObjects
                     where !condition
                     select myObject;

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

Я только что наткнулся на этот пост и решил поделиться.

void RemoveAll(object condition)  
{

    bool found = false;

    foreach(object thisObject in objects)    
    {

        if (condition)    
        {    
            objects.Remove(thisObject);

            found = true;

            break; //exit loop    
        }     
     }

    // Call again recursively

    if (found) RemoveAll(condition);

}

Мне не нравится идея обратного цикла for, поскольку она работает только с определенными структурами данных.

В общем, я бы использовал второй метод и собирал элементы, подлежащие удалению, в отдельную коллекцию «подлежащих удалению».Если удаление может привести к тому, что существующие итерации станут недействительными (как это произойдет, например, с любой сбалансированной коллекцией деревьев), то я не вижу способа обойти это.

Единственный другой метод, который я иногда использовал, — это перезапуск всей итерации, когда вы найдете первый элемент, который нужно удалить.Если вы справитесь и не найдете элементов для удаления, функция будет завершена.Это неэффективно, но иногда необходимо, если удаление одного элемента из коллекции может изменить набор элементов, которые необходимо удалить.

У меня есть словарь, и я хочу удалить все значения.Когда каждое значение удаляется, оно удаляется из словаря, что создает обсуждаемую вами проблему.Я сделал следующее:

foreach (var o in dictionary.Values.ToList())
{
  o.Dispose();
}

Я понимаю, что, возможно, это уже мертво, но я всегда делаю это так:

foreach (MyObject myObject в MyListOfMyObjects)
{

если (условие) MyListOfMyObjects.Remove(myObject);

перерыв;

}

Объект удаляется, а затем цикл завершается, альт!

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top