Почему я получаю "Коллекция была изменена, операция перечисления может не выполняться", когда не изменялась перечисляемая коллекция?

У меня есть две коллекции строк: CollectionA - свойство StringCollection объекта, хранящегося в системе, а CollectionB - это List, сгенерированный во время выполнения. CollectionA необходимо обновить, чтобы соответствовать CollectionB, если есть какие-либо различия. Поэтому я разработал то, что, как я ожидал, был простым методом LINQ для выполнения удаления.

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

Но я получаю ошибку "Collection was modified; enumeration operation may not execute" для strDifferences... хотя это отдельная перечислимая из измененной коллекции! Первоначально я разработал это явно, чтобы уклониться от этой ошибки, поскольку моя первая реализация создаст ее (поскольку я перечислял через CollectionA и просто удалял, когда !CollectionB.Contains(str)). Может ли кто-нибудь пролить некоторое представление о том, почему это перечисление терпит неудачу?

Ответы

Ответ 1

Эти два не являются полностью отдельными. Where не создает отдельную копию коллекции. Он внутренне сохраняет ссылку на исходную коллекцию и извлекает из нее элементы по мере их запроса.

Вы можете решить свою проблему, добавив ToList(), чтобы принудительно выполнить Where для немедленного итерации коллекции.

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

Ответ 2

Where возвращает IEnumerable<T>, а затем вы используете это в foreach (var strVar in strDifferences)

Затем вы пытаетесь удалить его из коллекции, создавшей IEnumerable<T>. Вы не создали новый список, ссылаясь на CollectionA, чтобы вытащить следующий элемент, поэтому вы не можете редактировать CollectionA.

Вы также можете сделать это:

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

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

Так как вы удаляете те, которые не находятся в CollectionB. Просто найдите те, которые есть, создайте список и назначьте этот список переменной CollectionA.

Ответ 3

Попробуйте немного изменить первую строку:

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

Я думаю, что LINQ использует ленивое выполнение вашего запроса. Вызов ToList приведет к выполнению запроса до перечисления.