Linq and deferred evaluation

女生的网名这么多〃 提交于 2019-12-10 14:39:39

问题


When you use LINQ to define an enumerable collection, either by using the LINQ extension methods or by using query operators,the application does not actually build the collection at the time that the LINQ extension method is executed; the collection is enumerated only when you iterate over it. This means that the data in the original collection can change between executing a LINQ query and retrieving the data that the query identifies; you will always fetch the most up-to-date data.

Microsoft Visual C# 2013 step by step written by John Sharp

I have written the following code:

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 + ", ");

The result of the above code is shown bellow:

1, 2, 3, 4, 5,

Why is that going like this? I know about Func, Action and Predicate but I can not figure out what is going on here. Based on the above definition the code is not rational.


回答1:


Apart from the ToList() at the end, which is creating a new collection, you have another issue.

The problem is that you are not using LINQ at all.

FindAll is not a LINQ extension method.

You should use 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 + ", ");



回答2:


First you set an int type list that contains 1,2,3,4,5 . then you used linq to create and define an enumeration collection . here describes how the linq work : first find all numbers that they are bigger than zero, as you see all of the items in the above list are bigger than zero , then select all of them and put them in a list . when you add 99 to the number list it doesn't effect on the enumeration collection that defined because it'll create a new collection and pass the items in it and it hasn't any references to the numbers list . you can delete .ToList() at the end of the linq expression, it'll result in : 1,2,3,4,5,99 .

Good Luck




回答3:


ToList creates a new instance of List<T> and copy all the items into it:

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);
 }

So if you want to have 99 in the res you should add it into res, not into numbers:

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

 res.Add(99);

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



回答4:


The ToList() is not actually the only problem. FindAll returns a new List. So when you call

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

That is the same as doing

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

So when you add a new item to numbers, it is no longer relevant. You are searching against the list returned by FindAll rather than the original list.




回答5:


You will see the result you expect if you defer (or remove all together) the ToList() operation until your foreach loop. ToList will execute the Linq expression the same as enumerating.

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 + ", ");


来源:https://stackoverflow.com/questions/39004141/linq-and-deferred-evaluation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!