C # предотвращение коллекции был изменен исключение - PullRequest
10 голосов
/ 15 февраля 2011

Является ли

 foreach(T value in new List<T>(oldList) )

опасным (дорогостоящим), когда oldList содержит 1 миллион объектов T?

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

Ответы [ 10 ]

8 голосов
/ 15 февраля 2011

Общее правило: вы не должны изменять ту же коллекцию, в которой вы перечисляете.Если вы хотите сделать что-то подобное, сохраните другую коллекцию, которая будет отслеживать, какие элементы добавить / удалить из исходной коллекции, а затем после выхода из цикла выполните операцию добавления / удаления в исходной коллекции.

5 голосов
/ 15 февраля 2011

просто так

var itemsToBeRemoved = new List<T>();

foreach (T item in myHugeList) 
{
    if (/*<condition>*/)
         itemsToBeRemoved.Add(item);
}

myHugeList.RemoveRange(itemsToBeRemoved);
4 голосов
/ 15 февраля 2011

Я обычно просто создаю список для всех объектов, которые будут удалены или добавлены.

Внутри foreach я просто добавляю элементы в соответствующие коллекции и изменяю исходную коллекцию после завершения foreach (цикл по коллекции removeItems и addItems)

1 голос
/ 10 декабря 2016

Если вы используете цикл Foreach для изменения коллекции, вы получите эту ошибку, как показано ниже.

List<string> li = new List<string>();
    li.Add("bhanu");
    li.Add("test");

    foreach (string s in li)
    {
        li.Remove(s);
    }

Решение - используйте For Loop, как показано ниже.

for (int i = 0; i < li.Count; i++)
    {
        li.RemoveAt(i);
        i--;
    }
1 голос
/ 15 февраля 2011

Если вы имеете в виду, что вы можете добавлять / удалять объекты из другого потока, я бы: 1 - синхронизировал потоки 2 - в добавлении / удалении потоков, создал список элементов для добавления или удаления 3 - и затем удалил эти элементыв критическом разделе (поэтому он небольшой - вам не нужно синхронизировать при добавлении элементов в список удаления)

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

0 голосов
/ 23 ноября 2017

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

/// где вы перечисляете

isBeingEnumerated = true
foreach(T value in new List<T>(oldList) )
isBeingEnumerated = false
SyncList(oldList with temporaryList)

///, где вы изменяете при перечислении

if isBeingEnumerated then
use a temporaryList to make the changes.
0 голосов
/ 28 июля 2016

foreach (значение T в новом List (oldList) .ToList ()) - попробовать

0 голосов
/ 15 февраля 2011

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

Вы можете реализовать его так, чтобы вы отмечали объекты для создания, обновления или удаления, а затем вызывали «SaveChanges», «Commit» или любую другую, выполняющую «применить изменения», и вы получитеготово.

Например, вы перебираете перечисляемый объект (oldList) и помечаете их как «удалить».Позже вы вызываете «SaveChanges», и более абстрактная, общая единица работы будет перебирать небольшой отфильтрованный список объектов для работы.

Во всяком случае, избегайте списков миллионов предметов.Вы должны работать с постраничными списками объектов.

0 голосов
/ 15 февраля 2011

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

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

   ...

   if(itemRemoveCondition) {
     oldList.RemoveAt(i--);
   }
}
0 голосов
/ 15 февраля 2011

Это будет «медленно», но вы ничего не можете с этим поделать, кроме как запустить его в фоновом потоке. Например. используя BackgroundWorker .

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

Если вы используете несколько потоков, вам придется изучить многопоточное программирование, и, например, используйте блокировки или, возможно, лучше ReaderWriterLock .

UPDATE: Как уже упоминалось в вопросе переполнения стека , теперь это возможно без каких-либо усилий в .NET 4.0 при использовании одновременных коллекций .

...