2011-02-15 4 views
7

лиC# предотвращение Коллекции Изменённого исключения

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

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

Больше Generaly, что это лучший способ перечислить более oldList, учитывая, что элементы могут быть добавлены/удалены во время перечисления ...

+0

Похоже, что есть два разных вопроса здесь. Я не знаю, почему они были объединены. –

ответ

4

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

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

7

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

+0

Хорошо, но на самом деле это другой поток, который может изменить коллекцию. По какой-то причине я не могу синхронизировать итерацию с добавлением/удалением. – Toto

+0

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

0

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

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

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

ОБНОВЛЕНИЕ: Как уже упоминалось в другом Stack Overflow question, это возможно без каких-либо усилий в .NET 4.0 when using concurrent collections.

0

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

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

    ... 

    if(itemRemoveCondition) { 
    oldList.RemoveAt(i--); 
    } 
} 
+1

Я считаю, что это путают; почему бы просто не прокрутить назад по списку? Тогда вам не нужно будет увеличивать и уменьшать индекс. –

+0

У меня возникла проблема с добавлением элементов. –

+0

Определяется ли конечное условие для цикла на каждом итератине или только один раз? – Toto

1

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

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

+0

По какой-то причине я не могу синхронизировать итерацию с добавлением/удалением. Итерация, безусловно, является решением моей проблемы. – Toto

+0

Я рад помочь. Если бы это было решением вашей проблемы, не могли бы вы пометить ее как ответ, нажав «зеленый контрольный знак»? :) благодаря! – JSBach

4

, как эта

var itemsToBeRemoved = new List<T>(); 

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

myHugeList.RemoveRange(itemsToBeRemoved); 
0

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

Слышали ли вы о шаблоне Единицы работы?

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

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

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

-2

попробуйте найти словарь.

int [] temp = Dictionary.Keys.ToArray();

  foreach (var c in temp) 
      { 
       if (condition) 
       { 
       //Your codes 
       } 

       else 
       { 

        Dictionary.Remove(c); 
        //OR 
        Dictionary.Add(c); 
       } 
0

Еогеасп (T значение в новом списке (oldList) .ToList()) - дать попробовать

1

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

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

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

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

for (int i = 0; i < li.Count; i++) 
    { 
     li.RemoveAt(i); 
     i--; 
    } 
0

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

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

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. 
Смежные вопросы