2010-05-07 5 views
13

J'ai deux collections de chaînes: CollectionA est une propriété StringCollection d'un objet stocké dans le système, tandis que CollectionB est une liste générée lors de l'exécution. CollectionA doit être mise à jour pour correspondre à CollectionB s'il y a des différences. J'ai donc conçu ce que je pensais être une simple méthode LINQ pour effectuer la suppression.Pourquoi je reçois "La collection a été modifiée, l'opération d'énumération peut ne pas s'exécuter" si elle ne modifie pas la collection énumérée?

var strDifferences = CollectionA.Where(foo => !CollectionB.Contains(foo)); 
foreach (var strVar in strDifferences) { CollectionA.Remove(strVar); } 

mais je reçois une erreur "Collection was modified; enumeration operation may not execute" sur strDifferences ... même si elle est un dénombrable séparé de la collection en cours de modification! J'ai initialement conçu ce explicitement pour éviter cette erreur, car ma première implémentation le produirait (comme je l'énumérais à travers CollectionA et juste en supprimant quand !CollectionB.Contains(str)). Quelqu'un peut-il nous expliquer pourquoi cette énumération échoue?

Répondre

21

Les deux ne sont pas complètement séparés. Where ne fait pas une copie distincte de la collection. Il conserve en interne une référence à la collection d'origine et récupère les éléments à partir de celui-ci lorsque vous le demandez.

Vous pouvez résoudre votre problème en ajoutant ToList() pour forcer Where à parcourir la collection immédiatement.

var strDifferences = CollectionA 
    .Where(foo => !CollectionB.Contains(foo)) 
    .ToList(); 
+0

«ToArray()» ne serait-il pas meilleur ici? Il n'y a pas besoin d'utiliser 'List'. – svick

+0

@svick C'est quelque chose que je me suis demandé depuis un certain temps, mais ce serait une question que je poserais un autre jour. Pourvu qu'il n'a pas déjà été demandé, bien sûr -checkle- –

+1

@GraceNote attraper cela ici: http://stackoverflow.com/questions/1105990/is-it-better-to-call-tolist-or-toarray-in -linq-requêtes – nawfal

3

Where passe en arrière IEnumerable<T> puis vous utilisez ce dans votre foreach (var strVar in strDifferences)

Vous êtes alors essayez de le retirer de la collection qui a créé le IEnumerable<T>. Vous n'avez pas créé de nouvelle liste, elle fait référence à CollectionA pour extraire l'élément suivant, vous ne pouvez donc pas modifier CollectionA.

Vous pouvez le faire aussi:

var strDifferences = CollectionA.Where 
    (foo => CollectionB.Contains(foo)).ToList(); 

CollectionA = strDifferences; 
//or instead of reassigning CollectionA 
CollectionA.Clear(); 
CollectionA.AddRange(strDifferences); 

Puisque vous supprimez ceux qui ne sont pas Collectionb. Cherchez simplement ceux qui sont, créez une liste et assignez cette liste à la variable CollectionA.

+0

Ah ... J'adorerais utiliser votre alternative (aussi identique soit-elle), mais malheureusement, CollectionA est une propriété en lecture seule, donc je ne peux pas la définir. Vous donnez cependant une explication beaucoup plus détaillée, donc +1 pour cela. –

+0

@ccornet si vous ne pouvez pas le définir, vous pouvez l'effacer et y ajouter les éléments. – kemiller2002

+0

J'ai réfléchi longuement et durement. J'ai décidé de continuer avec l'approche de suppression. Je ne souhaite pas modifier CollectionA si aucune modification n'est nécessaire, la construction d'une liste de ce qui doit être enlevé rend ceci très rapide et simple à savoir (voir si strDifferences.Count> 0). Si je construis un tableau de l'objectif, je dois encore vérifier si CollectionA a quelque chose qui doit être retiré. –

3

Essayez de modifier la première ligne un peu:

var strDifferences = 
    CollectionA.Where(foo => !CollectionB.Contains(foo)).ToList(); 

Je pense que LINQ utilise l'exécution paresseuse de votre requête. L'appel à ToList forcera l'exécution de la requête avant l'énumération.

+0

Oui, et assurez-vous de ne pas le mettre sur deux lignes i..e. var strDifferencesTemp = CollectionA.Where (foo =>! CollectionB.Contains (toto)); var strDifferences = strDifferencesTemp.ToList(); Si vous le faites, la collection peut être modifiée par un autre thread entre ces deux lignes. – Markus

Questions connexes