2016-08-17 3 views
5

При использовании LINQ для определения перечислимой коллекции, либо с помощью методов расширения LINQ или с помощью операторов запросов, приложение фактически не строить коллекции в то время, что расширение LINQ метод выполнен; сбор перечисляется только тогда, когда вы перебираете его. Это означает, что данные в исходной коллекции могут изменяться между выполнением запроса LINQ и получением данных, которые идентифицирует запрос; вы всегда будете получать самые последние данные .Linq и отложенная оценка

Microsoft Visual C# 2013 шаг за шагом, написанная Джоном Sharp

Я написал следующий код:

List<int> numbers = new List<int>() { 1, 2, 3, 4, 5 }; 
IEnumerable<int> res = numbers.FindAll(a => a > 0).Select(b => b).ToList(); 
numbers.Add(99); 
foreach (int item in res) 
    Console.Write(item + ", "); 

В результате приведенного выше кода показан ниже:

1, 2, 3, 4, 5,

Почему так происходит? Я знаю о Func, Action и Predicate, но я не могу понять, что здесь происходит. Исходя из вышеприведенного определения, код не является рациональным.

+1

Попробуйте удалить 'ToList()' в конце –

+0

@MatiasCicero Я пробовал, но не имел никакого эффекта. – Media

+2

'ToList()' создает * новый * список ('res') и копирует все элементы, но вы добавляете в * old * one (' numbers') –

ответ

6

Помимо ToList() в конце, которое создает новую коллекцию, у вас есть еще одна проблема.

Проблема в том, что вы вообще не используете LINQ.

FindAll не является методом расширения LINQ.

Вы должны использовать Where:

List<int> numbers = new List<int>() { 1, 2, 3, 4, 5 }; 
IEnumerable<int> res = numbers.Where(a => a > 0); 

numbers.Add(99); 

foreach (int item in res) 
    Console.Write(item + ", "); 
+3

Это фактический преступник, и единственный полный ответ. Я полностью просмотрел «FindAll». –

1

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

List<int> numbers = new List<int>() { 1, 2, 3, 4, 5 }; 
IEnumerable<int> res = numbers.FindAll(a => a > 0).Select(b => b); 

numbers.Add(99); 

foreach (int item in res) 
    Console.Write(item + ", "); 

// another option, but not necessary in most cases... 
foreach (int item in res.ToList()) 
    Console.Write(item + ", "); 
+1

Все еще распечатывает '1, 2, 3, 4, 5,' –

+0

@GlennFerrie, как я упомянул в комментариях, удаление ToList() не дает соответствующего ответа, который должен быть включен 99. Я пробовал его в Visual Studio 2013 и не работает. – Media

2

Сначала вы установили список типов типа, который содержит 1,2,3,4,5. , то вы использовали linq для создания и определения коллекции перечислений. здесь описывает, как работает linq: сначала найдите все числа, которые больше нуля, так как вы видите, что все элементы в приведенном выше списке больше нуля, затем выберите все из них и поместите их в список. когда вы добавляете 99 в список номеров, это не влияет на коллекцию перечислений, которая определена, потому что она создаст новую коллекцию и передаст элементы в ней, и она не имеет ссылок на список чисел. вы можете удалить .ToList() в конце выражения linq, это приведет к: 1,2,3,4,5,99.

Good Luck

+0

На самом деле, я думаю, вы не заботились о дефферированной оценке. – Media

2

ToList создает экземпляр List<T>новый и скопировать все элементы в нем:

http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,e276d6892241255b

public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source) { 
    if (source == null) throw Error.ArgumentNull("source"); 
    return new List<TSource>(source); 
} 

Так что, если вы хотите иметь 99 в res вас следует добавить его в res, а не в numbers:

... 
var res = numbers 
    .Where(a => a > 0) // Filter out; Select is redundant 
    .ToList(); 

res.Add(99); 

Console.Write(string.Join(", ", res)); 
2

ToList() это на самом деле не единственная проблема. FindAll возвращает новый список. Так что, когда вы звоните

IEnumerable<int> res = numbers.FindAll(a => a > 0) 

То есть так же, как делают

IEnumerable<int> newList = new List<int>(); 
foreach (int old in numbers) { 
    if (old > 0) newList.Add(old); 
} 

Итак, когда вы добавляете новый элемент в количестве, это уже не актуально. Вы ищете список, возвращенный FindAll, а не исходный список.

Смежные вопросы