Linq和延期评估
当您使用LINQ定义可枚举集合时,无论是使用LINQ扩展方法还是使用查询运算符,应用程序实际上都不会在执行LINQ扩展方法时构建集合。 只有在迭代它时才会枚举该集合。 这意味着原始集合中的数据可以在执行LINQ查询和检索查询识别的数据之间进行更改; 您将始终获取最新的数据。
Microsoft Visual C#2013一步一步由John Sharp编写
我写了以下代码:
List numbers = new List() { 1, 2, 3, 4, 5 }; IEnumerable 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
但我无法弄清楚这里发生了什么。 基于上面的定义,代码是不合理的。
除了最后创建新集合的ToList()
之外,还有另一个问题。
问题是你根本就没有使用LINQ。
FindAll
不是LINQ扩展方法。
你应该使用Where
:
List numbers = new List () { 1, 2, 3, 4, 5 }; IEnumerable 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
的新实例并将所有项目复制到其中:
http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,e276d6892241255b
public static List ToList (this IEnumerable source) { if (source == null) throw Error.ArgumentNull("source"); return new List (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 res = numbers.FindAll(a => a > 0)
这跟做的一样
IEnumerable newList = new List (); foreach (int old in numbers) { if (old > 0) newList.Add(old); }
因此,当您向数字添加新项目时,它不再相关。 您正在搜索FindAll返回的列表而不是原始列表。
如果将ToList()
操作推迟(或一起删除ToList()
直到foreach循环,您将看到预期的结果。 ToList
将执行与枚举相同的Linq表达式。
List numbers = new List () { 1, 2, 3, 4, 5 }; IEnumerable 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 + ", ");