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

我写了下面的代码:

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,

为什么会这样?我知道 FuncActionPredicate,但我不知道这里发生了什么。根据上面的定义,代码是不合理的。

如果您将 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,2,3,4,5 的 int 类型列表。 然后您使用 linq 创建和定义枚举集合。 这里描述了 linq 是如何工作的: 首先找到所有大于零的数字,正如您看到上面列表中的所有项目都大于零,然后 select 所有这些并将它们放入列表中。当您将 99 添加到数字列表时,它不会影响定义的枚举集合,因为它将创建一个新集合并传递其中的项目并且它没有对数字列表的任何引用。 您可以在 linq 表达式的末尾删除 .ToList() ,它会导致: 1,2,3,4,5,99 .

祝你好运

ToList 创建 new 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);
 }

因此,如果你想在 res 中包含 99,你应该将其添加到 res,而不是 numbers:

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

 res.Add(99);

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

除了最后的 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 + ", ");

ToList() 实际上并不是唯一的问题。 FindAll returns 一个新列表。所以当你打电话给

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 返回的列表而不是原始列表。