那么,animals
将有资格收集每个方法的结束,所以严格地说你的陈述是错误的。 animals
有资格在非LINQ情况下尽快收集,因此您声明的要点是真实的。
确实每个存储器的使用都不相同。然而,这里有一个暗示:LINQ在内存使用方面通常更糟糕,但实际上它比其他类型的方法更经常地允许更好的内存使用(虽然有非LINQ方法可以做同样的LINQ方式,当我使用.NET2.0时,我非常喜欢这个特定问题的基本方法)。
让我们考虑两种方法,非LINQ第一:
var animals = new string[] { "cow", "rabbit", "newt", "ram" };
var filtered = new List<string>();
foreach (string animal in animals)
//at this point we have both animals and filtered in memory, filtered is growing.
if(animal.StartsWith("ra")) filtered.Add(animal);
//at this point animals is no longer used. While still "in scope" to the source
//code, it will be available to collection in the produced code.
AnimalProcessor ap = new AnimalProcessor(filtered);
//at this point we have filtered and ap in memory.
ap.Start();
//at this point ap and filtered become eligible for collection.
值得一提的两两件事。一个“有资格”收藏并不意味着收藏会在那个时候发生,只是它可以在将来的任何时候收集。二,如果一个对象还没有被再次使用(甚至在某些情况下使用它,但这是另一个细节层次),那么对象仍然在范围内时可能发生集合。范围规则与程序源相关,并且是程序编写时可能发生的事情(程序员可以添加使用该对象的代码),GC收集合格规则与编译的程序相关,并且是关于何时发生的事情的问题。程序是编写的(程序员可以添加这样的代码,但他们没有)。
现在让我们来看看LINQ情况:
var animals = new string[] { "cow", "rabbit", "newt", "ram" };
var filtered = from animal in animals
where animal.StartsWith("ra")
select animal;
// at this pint we have both animals and filtered in memory.
// filtered defined as a class that acts upon animals.
AnimalProcessor ap = new AnimalProcessor(filtered);
// at this point we have ap, filtered and animals in memory.
ap.Start();
// at this point ap, filtered and animals become eligible for collection.
所以这里在这种情况下,没有任何相关的对象都可以收集到了最后。
但是,请注意,filtered
永远不是一个大对象。在第一种情况下,filtered
是包含0到n个对象范围内某处的列表,其中n是animals
的大小。在第二种情况下,filtered
是根据需要在animals
上工作的对象,本身具有基本恒定的内存。
因此,非LINQ版本的峰值内存使用量会更高,因为会有一个点,其中animals
仍然存在,并且filtered
包含所有相关对象。由于animals
的大小随着程序的变化而增加,实际上非LINQ版本最可能首先遇到严重的内存不足,因为在非LINQ情况下峰值内存使用状况更糟糕。
需要考虑的另一件事是,在真实世界的情况下,我们有足够的物品来担心内存消耗,就像我们的源不会成为一个列表。考虑:
IEnumerable<string> getAnimals(TextReader rdr)
{
using(rdr)
for(string line = rdr.ReadLine(); line != null; line = rdr.ReadLine())
yield return line;
}
此代码读取一个文本文件并一次返回每一行。如果每一行都有一个动物的名字,我们可以用它代替var animals
作为我们的来源filtered
。
在这种情况下,虽然LINQ版本只有很少的内存使用(一次只需要一个动物名称在内存中),而非LINQ版本有更多的内存使用(加载每个动物名称在进一步行动之前“ra”进入记忆)。 LINQ版本最多也会在几毫秒后开始处理,而非LINQ版本必须首先加载所有内容,才能完成一项工作。
因此,LINQ版本可以愉快地处理千兆字节的数据,而不需要使用更多的内存,而不需要处理少数内存,而非LINQ版本可能会遇到内存问题。
最后,需要注意的是,这与LINQ本身没有任何关系,因为您使用LINQ的方法与不使用LINQ的方法之间的差异。为了使LINQ相当于非LINQ使用:
var filtered = (from animal in animals
where animal.StartsWith("ra")
select animal).ToList();
为了使非LINQ相当于LINQ使用
var filtered = FilterAnimals(animals);
在这里你还可以定义:
private static IEnumerable<string> FilterAnimals(IEnumerable<string> animals)
{
foreach(string animal in animals)
if(animal.StartsWith("ra"))
yield return animal;
}
哪使用.NET 2.0技术,但即使使用.NET 1.1(尽管使用更多代码),您也可以在创建衍生自IEnumerable
首先给你的印象是什么? – Jay 2010-11-29 15:07:07
@Jay当我使用调试器时,它没有进入查询,直到结果被使用,我以前不知道/。 – nan 2010-11-29 15:10:51
``动物`会采用非linq方法吗? 5月(可能天真)的理解是,局部变量超出范围(并且可以收集)在函数的结束括号之后,即在ap之后。Start()在两周后返回(除非它真的异步运行,在这种情况下,这个评论是空的)。 =) – Jens 2010-11-29 15:13:01