Ну, 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, скорее всего, столкнется с серьезным дефицитом памяти в первую очередь из-за того, что пиковое использование памяти хуже в non-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 версия использует намного больше памяти (загружая каждое имя животного, которое существует с " ра "в память перед дальнейшими действиями). Версия 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