Ну, 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
Будет ли «животные» собираться в методе non-linq? Май (возможно, наивное) понимание состоит в том, что локальные переменные выходят за рамки (и могут быть собраны) после закрывающей скобки функции, то есть после ap.Start() возвращается через две недели (если только он не запускается асинхронно, в том случае, когда этот комментарий остается пустым). =) – Jens 2010-11-29 15:13:01