2010-05-07 10 views
13

Mam dwie kolekcje ciągów: CollectionA jest właściwością StringCollection obiektu przechowywanego w systemie, natomiast CollectionB jest listą wygenerowaną w czasie wykonywania. CollectionA musi zostać zaktualizowany, aby pasował do CollectionB, jeśli występują jakiekolwiek różnice. Więc wymyśliłem to, czego oczekiwałem jako prostą metodę LINQ do wykonania usuwania.Dlaczego otrzymuję komunikat "Kolekcja została zmodyfikowana, operacja wyliczania może nie zostać wykonana", gdy nie modyfikuje wyliczonej kolekcji?

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

Ale ja otrzymuję błąd "Collection was modified; enumeration operation may not execute" na strDifferences ... mimo, że jest odrębną przeliczalny z kolekcji modyfikowane! Pierwotnie wymyśliłem ten jawnie, aby uniknąć tego błędu, ponieważ moja pierwsza implementacja go wygeneruje (tak jak wyliczyłem przez CollectionA i właśnie usunięto, gdy !CollectionB.Contains(str)). Czy ktokolwiek może rzucić trochę wgląd w przyczyny niepowodzenia tego wyliczenia?

Odpowiedz

21

Oba nie są całkowicie oddzielne. Where nie tworzy osobnej kopii kolekcji. Wewnętrznie zachowuje odwołanie do oryginalnej kolekcji i pobiera z niej elementy zgodnie z żądaniem.

Możesz rozwiązać swój problem, dodając ToList(), aby zmusić Where do iteracji w kolekcji natychmiast.

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

Czy "ToArray()" nie byłoby tu lepiej? Nie ma potrzeby używania 'List'. – svick

+0

@svick To jest coś, nad czym się zastanawiałem przez jakiś czas, ale byłoby to pytanie, które chciałbym zadać na inny dzień. Pod warunkiem, że nie zostało już zadane, oczywiście - chuck- –

+1

@GraceNote złapać tutaj: http://stackoverflow.com/questions/1105990/is-it-better-to-call-tolist-or-toarray-in -linq-queries – nawfal

3

Where przechodzi z powrotem IEnumerable<T> a następnie używasz że w foreach (var strVar in strDifferences)

Jesteś następnie próbuje usunąć go ze zbioru, który stworzył IEnumerable<T>. Nie utworzyłeś nowej listy, która odwołuje się do CollectionA, aby wyciągnąć następny element, więc nie możesz edytować CollectionA.

Można to zrobić także:

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

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

Od usuwasz te, które nie są w CollectionB. Poszukaj tych, które są, utwórz listę i przypisz tę listę do zmiennej CollectionA.

+0

Ah ... Chciałbym użyć twojej alternatywy (jednakowej identycznej), ale niestety CollectionA, jak się okazuje, jest własnością tylko do odczytu, więc nie mogę jej ustawić. Dajesz jednak dużo więcej dogłębnego wyjaśnienia, więc daj +1 za to. –

+0

@ccornet, jeśli nie można go ustawić, możesz go wyczyścić i dodać elementy z powrotem. – kemiller2002

+0

Myślałem o tym długo i ciężko. Postanowiłem nadal stosować metodę usuwania. Nie chcę modyfikować CollectionA, jeśli żadna zmiana nie jest konieczna, budowanie listy tego, co należy usunąć, sprawia, że ​​jest to bardzo szybkie i proste (zobacz, czy strDifferences.Count> 0). Jeśli zbuduję tablicę celów, nadal będę musiał sprawdzić, czy CollectionA ma coś, co należy usunąć. –

3

Spróbuj modyfikację pierwszej linii trochę:

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

myślę LINQ jest za pomocą lazy wykonanie zapytania. Wywołanie ToList wymusi wykonanie zapytania przed wyliczeniem.

+0

Tak, i pamiętaj, aby nie umieszczać go na dwóch liniach. var strDifferencesTemp = CollectionA.Where (foo =>! CollectionB.Contains (foo)); var strDifferences = strDifferencesTemp.ToList(); jeśli to zrobisz, kolekcja może zostać zmodyfikowana przez inny wątek pomiędzy tymi dwoma liniami. – Markus

Powiązane problemy