LINQ: Deferred Execution (Ejecución diferida): Cosas a tener en cuenta

Pues como dice el título, la ejecución diferida es una característica de algunos operadores de LINQ, qué sólo cuando se itera sobre los elementos del enumerable, se evalua la consulta (cuando se llama al método MoveNext) y no cuando se construye.

No todos los operadores de LINQ causan esta ejecución diferida, hay excepciones:

  1. Operadores que retornan un sólo elemento o un valor escalar, como First, Last, Count…
  2. Operadores de conversión como ToArray, ToList…

Vamos a ver todo esto con un ejemplo:

///Deferred Execution
 
List<string> names = new List<string>();
names.Add("Juan");
names.Add("Pepe");
names.Add("Carlos");
names.Add("Jacinto");
 
var greaterthanfour = from n in names
                      where n.Length > 4
                      select n;
 
names[2] = "Ciro";
 
foreach (string name in greaterthanfour)
    Console.WriteLine(name);
 
Console.Read();

 Sí observamos la consulta, en ella se obtienen los nombres cuya longitud es mayor de 4, en tal caso la respuesta debería ser:

Carlos y Jacinto, no?

Pues no, porque como hemos comentado, la consulta no se evalua hasta que se itera sobre ella (instrucción foreach), y por tanto Carlos ha sido sustituido por Ciro, con lo cual el resultado es:

linq1

Otra problema que nos podemos encontrar con la ejecución diferida es la Reevalución, es decir, la reevalución se produce cuando reenumeramos. Dada la siguiente consulta:

///Deferred Execution -> Reevaluation
 
List<string> names = new List<string>();
names.Add("Juan");
names.Add("Pepe");
names.Add("Carlos");
names.Add("Jacinto");
 
var greaterthanfour = from n in names
                      where n.Length > 4
                      select n;
 
Console.WriteLine("foreach 1");
 
foreach (string name in greaterthanfour)
    Console.WriteLine(name);
 
names.Clear();
 
Console.WriteLine("foreach 2");
 
foreach (string name in greaterthanfour)
    Console.WriteLine(name);
 
Console.Read();

En el primer foreach, obtendremos como resultado:

linq2

 Y en el segundo, no se muestra nada :S, porque hemos limpiado la lista y estamos volviendo a reenumerar, lo que conlleva una reevaluación:

linq3

Para evitar la reevaluación de expresiones (Dado que esta reevalución implica un costo adicional, sobre todo sí estamos ejecutando consultas sobre una base de datos), podemos utilizar métodos como ToArray, ToList… que causan la ejecución inmediata de la consulta y posteriormente trabajar sobre esa copia o caché:

///Deferred Execution -> Reevaluation -> Cache Names
 
List<string> namesCache = (from n in names
                           where n.Length > 4
                           select n).ToList();
 
Console.WriteLine("foreach 1");
 
foreach (string name in namesCache)
    Console.WriteLine(name);
 
names.Clear();
 
Console.WriteLine("foreach 2");
 
foreach (string name in namesCache)
    Console.WriteLine(name);
 
Console.Read();

Resultado:

linq4 

Y por último, otro problema que nos encontramos con la ejecución diferida, es cuando dentro una consulta LINQ usamos variables externas o Outer Variables. Veáse el siguiente ejemplo:

///Deferred Execution -> Outer Variables
 
List<int> salary = new List<int>();
salary.Add(1800);
salary.Add(1500);
 
int plus = 1000;
 
IEnumerable<int> totalSalary = salary.Select(s => s + plus);
 
plus = 500;
 
foreach (int total in totalSalary)
    Console.WriteLine(total);
 
Console.Read();

El problema en este caso, es que estamos usando una variable externa (plus) en la consulta, que inicilamente se incializa a 1000, con lo que podríamos esperar que el resultado fuera o fuese 2800 y 2500, pero antes de iterar, cambiamos su valor a 500, y es este último el que se utilizará para calcular el salario total:

linq5

Así que a partir de ahora, cuidado con la ejecución diferida ;)

Bibliografía: C# 3.0 in a Nutshell

Esto es todo!!!

Published 9/3/2009 19:36 por Luis Ruiz Pavón
Archivado en: ,,,
Comparte este post:
http://geeks.ms/blogs/lruiz/archive/2009/03/09/linq-deferred-execution-ejecuci-243-n-diferida-cosas-a-tener-en-cuenta.aspx

Comentarios

# [ASP.NET vNext] Model Binding: Obteniendo datos

Siguiendo con la serie de novedades que Scott Guthrie está publicando sobre ASP.NET vNext , esta vez

Tuesday, September 06, 2011 11:05 AM por Amigo mío Siempre estas Programando en .NET