给出以下代码:
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
//Init data
char[] chars = new char[10];
FillData(chars);
// Write the initial data
PrintContents("Initial data:", chars);
//Take some data:
IEnumerable<char> acc = chars.Take(3);
//View data
PrintContents("Enum:", acc);
//Edit data
chars[0] = 'z';
chars[1] = 'z';
chars[2] = 'z';
//View data again
PrintContents("Enum after modifing source:", acc);
//Restart data
chars = new char[5];
FillData(chars);
//View data when source is replaced
PrintContents("Enum after new source:", acc);
}
//Gets a ref
private static void FillData(char[] data)
{
for(int i = 0; i < data.Length; i++)
{
data[i] = (char)('a' + i);
}
}
private static void PrintContents(string what, IEnumerable<char> src)
{
System.Console.WriteLine(what);
string s = "";
foreach(char ch in src)
{
s += ch;
}
if(s.Length > 0)
{
System.Console.WriteLine(s);
}
}
}
我得到此输出:
Initial data:
abcdefghij
Enum:
abc
Enum after modifing source:
zzz
Enum after new source:
zzz
我知道推迟执行,但是这是预期的行为吗?这意味着我应该重用IEnumerable或IEnumerable上使用的任何数据,而无需创建新的集合,因为我可能会更改程序的结果。
这意味着IEnumerable也将保留对数据源的引用,即使可见代码也未使用它们,并且在将要收集IEnumerable本身之前不会进行垃圾收集。
我最近在一个项目上使用IEnumerable很多,看到的越多,我就越不喜欢它们。不要误会我的意思,Linq做的很棒,但是我希望它有时返回相同类型的源。
是,这是预期的行为。
您应将LINQ方法的结果视为“我枚举时的计算结果”,而不是“项的集合”。对我来说,更容易理解,当我第二次枚举它时,它将在我遍历项目时再次计算结果。
在源数据可能发生更改的情况下(例如问题中的示例)或获取结果的成本很高(在查询数据库是隐藏成本的非常常见的情况下,这非常重要)。不幸的是,没有通用的方法来阐明可枚举是昂贵的(即DB)还是本质上是免费的(即列表),并且两种情况都经常使用-重复查询实时数据或重复枚举缓存的结果。
您担心查询使数据源保持活动状态的时间可能比您预期的长-是的,这是一个问题。您应该了解结果的预期用途,并考虑返回非惰性结果是否更好(即.ToList()
)。例如,您应该强烈考虑将非惰性枚举数传递给ASP.Net MVC视图-数据可以很容易地迭代多次以进行渲染(即使.Count()
是一个迭代),因此在数据库上延迟计算的可枚举可以轻松地使渲染成本增加一倍或三倍。这一页。