Каковы преимущества отложенного выполнения в LINQ? - PullRequest
39 голосов
/ 06 сентября 2011

LINQ использует модель отложенного выполнения, которая означает, что результирующая последовательность не возвращается во время вызова операторов Linq, но вместо этого эти операторы возвращают объект, который затем возвращает элементы последовательности только при перечислении этого объекта.

Хотя я понимаю, как работают отложенные запросы, у меня возникают проблемы с пониманием преимуществ отложенного выполнения:

1) Я прочитал, что отложенный запрос, выполняемый только тогда, когда вам действительно нужны результаты, может иметьбольшое преимуществоТак что же это за преимущество?

2) Другое преимущество отложенных запросов заключается в том, что если вы определяете запрос один раз, то при каждом перечислении результатов вы будете получать разные результаты, если данные изменятся.

a) Но, как видно из кода ниже, мы можем достичь того же эффекта (таким образом, каждый раз, когда мы перечисляем ресурс, мы получаем другой результат, если данные изменились), даже не используя отложенные запросы:

List<string> sList = new List<string>( new[]{ "A","B" });

foreach (string item in sList)
    Console.WriteLine(item); // Q1 outputs AB

sList.Add("C");

foreach (string item in sList)
    Console.WriteLine(item); // Q2 outputs ABC

3) Есть ли другие преимущества отложенного исполнения?

Ответы [ 3 ]

47 голосов
/ 06 сентября 2011

Основным преимуществом является то, что это позволяет фильтрующим операциям, ядру LINQ, быть намного более эффективным.(Это фактически ваш элемент № 1).

Например, возьмите запрос LINQ, подобный следующему:

 var results = collection.Select(item => item.Foo).Where(foo => foo < 3).ToList();

При отложенном выполнении вышеприведенное повторяет вашу коллекцию один раз , и каждый раз, когда элемент запрашивается во время итерации, выполняет операцию сопоставления, фильтрует, а затем использует результаты для построения списка.

Если вы должны были полностью выполнить LINQ каждый раз, каждая операция (Select / Where) придется перебирать всю последовательность.Это сделало бы цепные операции очень неэффективными.

Лично я бы сказал, что ваш пункт № 2 выше является скорее побочным эффектом, а не выгодой - хотя иногда он выгоден, он также вызывает некоторую путаницу враз, так что я бы просто рассмотрел это «что-то, чтобы понять», а не рекламировать это как преимущество LINQ.


В ответ на ваше редактирование:

В вашем конкретномНапример, в обоих случаях Select выполняет итерацию коллекции и возвращает IEnumerable I1 типа item.Foo.Затем Where () будет перечислять I1 и возвращать IEnumerable <> I2 типа item.Foo.Затем I2 будет преобразован в список.

Это не так - отложенное выполнение предотвращает это.

В моем примере тип возвращаемого значения IEnumerable<T>, что означает, чтоэто коллекция, которую можно перечислить , но из-за отложенного выполнения она фактически не перечисляется.

Когда вы вызываете ToList(), перечисляется вся коллекция.Результат в конечном итоге выглядит концептуально более похожим (хотя, конечно, иным):

List<Foo> results = new List<Foo>();
foreach(var item in collection)
{
    // "Select" does a mapping
    var foo = item.Foo; 

    // "Where" filters
    if (!(foo < 3))
         continue;

    // "ToList" builds results
    results.Add(foo);
}

Отложенное выполнение приводит к тому, что сама последовательность перечисляется только (foreach) один раз , когдаиспользуется (ToList()).Без отложенного выполнения это выглядело бы больше (концептуально):

// Select
List<Foo> foos = new List<Foo>();
foreach(var item in collection)
{
    foos.Add(item.Foo);
}

// Where
List<Foo> foosFiltered = new List<Foo>();
foreach(var foo in foos)
{
    if (foo < 3)
        foosFiltered.Add(foo);
}    

List<Foo> results = new List<Foo>();
foreach(var item in foosFiltered)
{
    results.Add(item);
}
36 голосов
/ 06 сентября 2011

Еще одним преимуществом отложенного выполнения является то, что оно позволяет работать с бесконечными сериями.Например:

public static IEnumerable<ulong> FibonacciNumbers()
{
    yield return 0;
    yield return 1;

    ulong previous = 0, current = 1;
    while (true)
    {
        ulong next = checked(previous + current);
        yield return next;
        previous = current;
        current = next;

    }
}

(Источник: http://chrisfulstow.com/fibonacci-numbers-iterator-with-csharp-yield-statements/)

. Затем можно сделать следующее:

var firstTenOddFibNumbers = FibonacciNumbers().Where(n=>n%2 == 1).Take(10);
foreach (var num in firstTenOddFibNumbers)
{
    Console.WriteLine(num);
}

Отпечатки:

1
1
3
5
13
21
55
89
233
377

Без отложенного выполнения вы получите OverflowException или, если бы операция не была checked, она бы выполнялась бесконечно, потому что она оборачивается (и если бы вы вызвали ToList, это в конечном итоге привело бы к OutOfMemoryException)

6 голосов
/ 14 апреля 2013

Важным преимуществом отложенного выполнения является то, что вы получаете самые свежие данные.Это может повлиять на производительность (особенно если вы имеете дело с абсурдно большими наборами данных), но в равной степени данные могут измениться к тому времени, когда ваш исходный запрос вернет результат.Отложенное выполнение гарантирует, что вы получите самую свежую информацию из базы данных в тех случаях, когда база данных быстро обновляется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...