Linq和延期评估

本文关键字:评估 Linq | 更新日期: 2023-09-27 18:03:01

使用LINQ定义可枚举集合时,无论是使用LINQ扩展方法还是使用查询运算符,应用程序在LINQ执行扩展方法;只有当您对其进行迭代。这意味着原始中的数据集合可以在执行LINQ查询和检索之间更改查询所标识的数据;你总是能拿到最多的最新数据

Microsoft Visual C#2013由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,但我不知道这里发生了什么。基于上述定义,代码是不合理的。

Linq和延期评估

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

首先设置一个包含1,2,3,4,5的int类型列表。然后使用linq创建并定义了一个枚举集合。这里描述了linq的工作方式:首先找到所有大于零的数字,因为你看到上面列表中的所有项目都大于零,然后选择所有项目并将它们放在列表中。当您将99添加到数字列表中时,它不会影响定义的枚举集合,因为它将创建一个新集合并传递其中的项,并且它没有任何对数字列表的引用。您可以删除。在linq表达式末尾的ToList((,它将导致:1,2,3,4,5,99。

好运

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

因此,如果你想在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()实际上并不是唯一的问题。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返回的列表而不是原始列表进行搜索。

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