2011-02-15 5 views
7

Est-ce queC# prévention Collection a été modifiée exception

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

est dangereux (coûteuse) lorsque oldList contient 1 millions de T objet?

Plus quelle est la generaly meilleure façon d'énumérer sur oldList étant donné que les éléments peuvent être ajoutés/supprimés lors de l'énumération ...

+0

Il semble qu'il y ait ici deux questions différentes. Je ne sais pas pourquoi ils ont été combinés. –

Répondre

4

En général, je crée une liste de tous les objets à supprimer ou ajoutés.

Dans le foreach je viens d'ajouter les articles aux collections appropriées et de modifier la collection originale après la foreach ont terminé (boucle à travers la collection removeItems et addItems)

7

La règle générale est, vous ne devriez pas modifier le même collection dans laquelle vous êtes en train d'énumérer. Si vous voulez faire quelque chose comme ça, gardez une autre collection qui gardera trace des éléments à ajouter/retirer de la collection originale et ensuite, après avoir quitté la boucle, effectuez l'opération d'ajout/suppression sur la collection originale.

+0

Ok mais en fait c'est un autre thread qui peut modifier la collection. Pour une raison quelconque, je ne peux pas synchroniser l'itération avec l'addition/suppression. – Toto

+0

Comme mentionné dans ma réponse mise à jour, si vous utilisez .NET 4.0, envisagez d'utiliser des collections simultanées. –

0

Ce sera 'lent' mais il n'y a pas grand chose de plus à faire à ce sujet, excepté de l'exécuter sur un thread d'arrière-plan. Par exemple. en utilisant un BackgroundWorker. Si vos opérations sur la liste ne se produisent que sur un thread, l'approche correcte consiste à ajouter les éléments à ajouter/supprimer dans des listes séparées et à effectuer ces opérations après la fin de vos itérations.

Si vous utilisez plusieurs threads, vous devrez vous intéresser à la programmation multithread, et par ex. utilisez locks ou probablement mieux un ReaderWriterLock.

MISE À JOUR: Comme mentionné dans un autre Stack Overflow question, cela est maintenant possible sans aucun effort dans .NET 4.0 when using concurrent collections.

0

Vous pouvez parcourir la liste sans l'aide d'un recenseur, donc faire quelque chose comme ...

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

    ... 

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

Je trouve ce genre de confusion; pourquoi ne pas simplement rebondir sur la liste? Vous n'avez alors pas besoin de mélanger l'incrémentation et la décrémentation de l'index. –

+0

Je vous boucle en arrière vous obtenez un problème avec l'ajout d'éléments. –

+0

Est-ce que la condition finale de la boucle est évaluée à chaque itératie ou juste une fois? – Toto

1

Si vous voulez dire que vous pouvez ajouter/supprimer des objets d'un autre thread, je: 1-synchroniser la threads 2- dans les threads ajouter/supprimer, créer une liste d'éléments à ajouter ou à supprimer 3- puis supprimer ces éléments dans une section critique (donc il est petit - vous n'avez pas besoin de synchroniser lors de l'ajout des éléments à la liste de suppression)

Si vous ne voulez pas faire cela, vous pouvez utiliser pour au lieu de fo atteindre, qui éviterait l'exception, mais vous devez prendre des précautions supplémentaires afin de ne pas les autres types d'exceptions

+0

Pour une raison quelconque, je ne peux pas synchroniser l'itération avec l'addition/suppression. L'itération est certainement la solution à mon problème. – Toto

+0

Je suis heureux d'aider. Si c'était la solution à votre problème, pourriez-vous s'il vous plaît marquer comme réponse, en cliquant sur le "signe de vérification vert"? :) Merci! – JSBach

4

comme ce

var itemsToBeRemoved = new List<T>(); 

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

myHugeList.RemoveRange(itemsToBeRemoved); 
0

Pour moi, la première chose est que vous devriez envisager d'utiliser certains type de pagination de données, car avoir une telle liste de 1 million d'éléments peut être dangereuse.

Avez-vous entendu parler du modèle d'unité de travail?

Vous pouvez l'implémenter de sorte que vous marquez des objets à créer, mettre à jour ou supprimer, et plus tard, vous appelez "SaveChanges", "Commit" ou tout autre travail "apply changes", et vous aurez terminé. Par exemple, vous itérez sur l'énumérable (oldList) et vous les marquez comme "supprimer". Plus tard, vous appelez "SaveChanges" et l'unité de travail générique, plus abstraite, parcourra la petite liste filtrée d'objets avec lesquels travailler.

Quoi qu'il en soit, éviter les listes d'un milion articles. Vous devriez travailler avec des listes d'objets paginés.

-2

Pour le dictionnaire.

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

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

       else 
       { 

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

foreach (valeur T dans la nouvelle liste (oldList) .ToList()) - donner un essai

1

Si vous utilisez la boucle Foreach pour modifier la collection, alors vous obtiendrez cette erreur comme ci-dessous.

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

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

Solution - utiliser For Loop comme ci-dessous.

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

Vous pouvez utiliser un indicateur pour passer de la modification à une liste temporaire pendant l'énumération de l'original.

/// où vous énumérez

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

/// où vous modifiez tout énumérant

if isBeingEnumerated then 
use a temporaryList to make the changes. 

Questions connexes