¿Cuáles son los beneficios de una ejecución diferida en LINQ?

LINQ utiliza un modelo de ejecución diferida, lo que significa que la secuencia resultante no se devuelve en el momento en que se llama a los operadores Linq, sino que estos operadores devuelven un objeto que luego produce elementos de una secuencia solo cuando enumeramos este objeto.

Si bien entiendo cómo funcionan las consultas diferidas, tengo problemas para comprender los beneficios de la ejecución diferida:

1) He leído que la consulta diferida que se ejecuta solo cuando realmente se necesitan los resultados puede ser de gran beneficio. Entonces, ¿qué es este beneficio?

2) Otra ventaja de las consultas diferidas es que si define una consulta una vez, cada vez que enumere los resultados, obtendrá diferentes resultados si los datos cambian.

a) Pero como se ve en el código a continuación, podemos lograr el mismo efecto (por lo tanto, cada vez que enumeramos el recurso, obtenemos un resultado diferente si los datos cambian) incluso sin utilizar consultas diferidas:

List sList = new List( 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) ¿Hay otros beneficios de la ejecución diferida?

El principal beneficio es que esto permite que las operaciones de filtrado, el núcleo de LINQ, sean mucho más eficientes. (Este es efectivamente su artículo # 1).

Por ejemplo, tome una consulta LINQ como esta:

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

Con la ejecución diferida, lo anterior itera su colección una vez , y cada vez que se solicita un elemento durante la iteración, realiza la operación del mapa, filtra y luego utiliza los resultados para comstackr la lista.

Si tuviera que hacer que LINQ se ejecutara por completo cada vez, cada operación ( Select / Where ) tendría que iterar a través de toda la secuencia. Esto haría que las operaciones encadenadas fueran muy ineficientes.

Personalmente, diría que su ítem n. ° 2 anterior es más un efecto secundario que un beneficio, aunque a veces es beneficioso, también causa cierta confusión a veces, así que simplemente consideraría esto como "algo para entender" y no promocionarlo como un beneficio de LINQ.


En respuesta a su edición:

En su ejemplo particular, en ambos casos Select iteraría la colección y devolvería un IEnumerable I1 del tipo item.Foo. Where () enumeraría I1 y devolvería IEnumerable <> I2 del tipo item.Foo. I2 se convertiría a la Lista.

Esto no es cierto: la ejecución diferida impide que esto ocurra.

En mi ejemplo, el tipo de devolución es IEnumerable , lo que significa que es una colección que se puede enumerar , pero, debido a la ejecución diferida, en realidad no se enumera.

Cuando llama a ToList() , se enumera toda la colección. El resultado termina mirando conceptualmente algo más parecido (aunque, por supuesto, diferente):

 List results = new List(); 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); } 

La ejecución diferida hace que la secuencia en sí misma solo se enumere (foreach) una vez , cuando se usa (por ToList() ). Sin ejecución diferida, se vería más como (conceptualmente):

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

Otro beneficio de la ejecución diferida es que le permite trabajar con series infinitas. Por ejemplo:

 public static IEnumerable 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; } } 

(Fuente: http://chrisfulstow.com/fibonacci-numbers-iterator-with-csharp-yield-statements/ )

A continuación, puede hacer lo siguiente:

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

Huellas dactilares:

1
1
3
5
13
21
55
89
233
377

Sin ejecución diferida, obtendría una OverflowException o si la operación no se checked , se ejecutaría infinitamente porque se ajusta (y si llamara a ToList , ocasionaría una OutOfMemoryException eventualmente)

Un beneficio importante de la ejecución diferida es que recibe datos actualizados. Esto puede ser un golpe en el rendimiento (especialmente si se trata de conjuntos de datos absurdamente grandes) pero igualmente los datos pueden haber cambiado en el momento en que su consulta original devuelva un resultado. La ejecución diferida asegura que obtendrá la información más reciente de la base de datos en escenarios donde la base de datos se actualiza rápidamente.