Это может быть довольно тривиально, но я иногда не могу этого понять. Мне нужно удалить некоторые предметы из коллекции. У меня есть список моей модели, который заполняется из базы данных. Далее я извлек различные коллекции из этого списка, а затем попытался удалить некоторые элементы, но это не сработало так, как я думал

List<MyModel> temp = DbHelper.Select("Tree"); // count of temp is 40
var _doc = temp.Where(x => x.Parent == code + "02"); // count of _doc is 9
// type of _doc is System.Linq.Enumerable.WhereListIterator<MyModel>
var cd = _doc.ToList().RemoveAll(x => x.S7 == "CDFL"); // cd is 2

Поскольку cd имеет значение 2, из _doc следует удалить 2 элемента. Но в _doc все еще есть 9 элементов.

Но затем я изменил тип _doc на список следующим образом:

var _doc = temp.Where(x => x.Parent == code + "02").ToList(); // count of _doc is 9
// type of _doc is List<MyModel>
var cd = _doc.RemoveAll(x => x.S7 == "CDFL"); // cd is 2

И счетчик _doc равен 7, что правильно.

RemoveAll вызывается для типа List в обоих случаях, поэтому почему поведение в обоих случаях отличается?

1
Awais Mahmood 26 Ноя 2016 в 13:05

2 ответа

Лучший ответ

Эта следующая строка кода

var _doc = temp.Where(x => x.Parent == code + "02").ToList(); 

Создает новый список из элементов temp, прошедших фильтрацию.

Затем в следующей строке

var cd = _doc.RemoveAll(x => x.S7 == "CDFL"); 

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

Тогда как в следующей строке

var _doc = temp.Where(x => x.Parent == code + "02");

Вы просто определяете запрос linq и не заставляете его выполняться. Типом _doc теперь является IEnumerable<T>, где T - это тип x. Чтобы увидеть, какие элементы проходят этот фильтр, вы должны принудительно выполнить его. Это можно сделать разными способами . Некоторые из них следующие

  • Путем прокрутки этой последовательности с помощью оператора foreach.
  • Позвонив ToList
  • Позвонив ToArray

Теперь критическая точка находится в следующей строке:

var cd = _doc.ToList().RemoveAll(x => x.S7 == "CDFL"); 

Давайте по частям. Как я упоминал выше, _doc.ToList() создаст новый Lit<T>. Затем вы применяете RemoveAll в этом списке.

Вот в чем разница . _doc будет иметь ту же ссылку, что и до того, как вы вызвали ToList. Кроме того, RemoveAll был бы применен к списку, который был создан в результате вызова ToList.

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

1
Christos 26 Ноя 2016 в 10:28

Согласно msdn:

Метод ToList (IEnumerable) вызывает немедленную оценку запроса и возвращает список, содержащий результаты запроса. Вы можете добавить этот метод в свой запрос, чтобы получить кэшированную копию результатов запроса.

Итак, в первом случае вы удаляете элементы из нового экземпляра List, и именно поэтому эти элементы не удаляются из коллекции _doc.

1
mrlukz 26 Ноя 2016 в 10:28