简体   繁体   English

Linq和延期评估

[英]Linq and deferred evaluation

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; 当您使用LINQ来定义可枚举集合时,无论是使用LINQ扩展方法还是使用查询运算符,应用程序实际上都不会在执行LINQ扩展方法时构建集合。 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; 这意味着原始集合中的数据可以在执行LINQ查询和检索查询标识的数据之间发生变化。 you will always fetch the most up-to-date data. 您将始终获取最新的数据。

Microsoft Visual C# 2013 step by step written by John Sharp Microsoft Visual C#2013一步一步由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, 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. 我知道FuncActionPredicate但我无法弄清楚这里发生了什么。 Based on the above definition the code is not rational. 基于上面的定义,代码是不合理的。

Apart from the ToList() at the end, which is creating a new collection, you have another issue. 除了最后创建新集合的ToList()之外,还有另一个问题。

The problem is that you are not using LINQ at all. 问题是你根本就没有使用LINQ。

FindAll is not a LINQ extension method. FindAll不是LINQ扩展方法。

You should use Where : 你应该使用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 + ", ");

First you set an int type list that contains 1,2,3,4,5 . 首先设置一个包含1,2,3,4,5的int类型列表。 then you used linq to create and define an enumeration collection . 然后你使用linq来创建和定义枚举集合。 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 . 这里描述了linq如何工作:首先找到它们大于零的所有数字,因为你看到上面列表中的所有项都大于零,然后选择所有项并将它们放在一个列表中。 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 . 当您向数字列表中添加99时,它不会对定义的枚举集合产生影响,因为它将创建一个新集合并传递其中的项目,并且它没有对数字列表的任何引用。 you can delete .ToList() at the end of the linq expression, it'll result in : 1,2,3,4,5,99 . 你可以在linq表达式的末尾删除.ToList(),它将导致:1,2,3,4,5,99。

Good Luck 祝好运

ToList creates a new instance of List<T> and copy all the items into it: ToList创建List<T>实例并将所有项目复制到其中:

http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,e276d6892241255b 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 : 因此,如果你想在res99 ,你应该将它添加到res ,而不是numbers

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

 res.Add(99);

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

The ToList() is not actually the only problem. ToList()实际上不是唯一的问题。 FindAll returns a new List. FindAll返回一个新列表。 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. 您正在搜索FindAll返回的列表而不是原始列表。

You will see the result you expect if you defer (or remove all together) the ToList() operation until your foreach loop. 如果将ToList()操作推迟(或一起删除ToList()直到foreach循环,您将看到预期的结果。 ToList will execute the Linq expression the same as enumerating. 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 + ", ");

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM