Подтвердить что ты не робот

Каковы преимущества отложенного выполнения в LINQ?

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) Существуют ли другие преимущества отсроченного исполнения?

4b9b3361

Ответ 1

Основное преимущество заключается в том, что это позволяет выполнять более эффективные операции фильтрации, ядро ​​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);
}

Ответ 2

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

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 в конечном итоге)

Ответ 3

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