linq
Modos de ejecución de métodos: transmisión inmediata, diferida, sin transmisión diferida
Buscar..
Ejecución diferida vs ejecución inmediata
Algunos métodos LINQ devuelven un objeto de consulta. Este objeto no contiene los resultados de la consulta; en cambio, tiene toda la información necesaria para generar esos resultados:
var list = new List<int>() {1, 2, 3, 4, 5};
var query = list.Select(x => {
Console.Write($"{x} ");
return x;
});
La consulta contiene una llamada a Console.Write
, pero no se ha Console.Write
nada a la consola. Esto se debe a que la consulta aún no se ha ejecutado y, por lo tanto, la función pasada a Select
nunca se ha evaluado. Esto se conoce como ejecución diferida: la ejecución de la consulta se retrasa hasta algún punto posterior.
Otros métodos LINQ obligan a una ejecución inmediata de la consulta; Estos métodos ejecutan la consulta y generan sus valores:
var newList = query.ToList();
En este punto, la función que pasó a Select
se evaluará para cada valor en la lista original, y lo siguiente se enviará a la consola:
1 2 3 4 5
En general, los métodos LINQ que devuelven un solo valor (como Max
o Count
), o que devuelven un objeto que realmente contiene los valores (como ToList
o ToDictionary
) se ejecutan inmediatamente.
Los métodos que devuelven un IEnumerable<T>
o IQueryable<T>
devuelven el objeto de consulta y permiten aplazar la ejecución hasta un punto posterior.
Si un método LINQ particular obliga a una consulta a ejecutarse inmediatamente o no, se puede encontrar en MSDN - C # , o VB.NET .
Modo de transmisión (evaluación perezosa) versus modo sin transmisión (evaluación impaciente)
De los métodos LINQ que utilizan la ejecución diferida, algunos requieren que se evalúe un solo valor a la vez. El siguiente código:
var lst = new List<int>() {3, 5, 1, 2};
var streamingQuery = lst.Select(x => {
Console.WriteLine(x);
return x;
});
foreach (var i in streamingQuery) {
Console.WriteLine($"foreach iteration value: {i}");
}
saldrá:
3
valor de iteración de foreach: 3
5
valor de iteración de foreach: 5
1
valor de iteración de foreach: 1
2
valor de iteración de foreach: 2
porque la función pasada a Select
se evalúa en cada iteración del foreach
. Esto se conoce como modo de transmisión o evaluación perezosa .
Otros métodos LINQ (operadores de clasificación y agrupación) requieren que se evalúen todos los valores, antes de que puedan devolver cualquier valor:
var nonStreamingQuery = lst.OrderBy(x => {
Console.WriteLine(x);
return x;
});
foreach (var i in nonStreamingQuery) {
Console.WriteLine($"foreach iteration value: {i}");
}
saldrá:
3
5
1
2
valor de iteración de foreach: 1
valor de iteración de foreach: 2
valor de iteración de foreach: 3
valor de iteración de foreach: 5
En este caso, debido a que los valores se deben generar para el foreach
en orden ascendente, primero se deben evaluar todos los elementos, para determinar cuál es el más pequeño, y cuál es el siguiente más pequeño, y así sucesivamente. Esto se conoce como modo sin transmisión o evaluación impaciente .
Ya sea que un método LINQ particular use el modo de transmisión por secuencias o no, puede encontrarse en MSDN - C # , o VB.NET .
Beneficios de la ejecución diferida - construcción de consultas
La ejecución diferida permite combinar diferentes operaciones para construir la consulta final, antes de evaluar los valores:
var list = new List<int>() {1,1,2,3,5,8};
var query = list.Select(x => x + 1);
Si ejecutamos la consulta en este punto:
foreach (var x in query) {
Console.Write($"{x} ");
}
obtendríamos la siguiente salida:
2 2 3 4 6 9
Pero podemos modificar la consulta agregando más operadores:
Console.WriteLine();
query = query.Where(x => x % 2 == 0);
query = query.Select(x => x * 10);
foreach (var x in query) {
Console.Write($"{x} ");
}
Salida:
20 20 40 60
Beneficios de la ejecución diferida - consultar datos actuales
Con la ejecución diferida, si se cambian los datos a consultar, el objeto de consulta utiliza los datos en el momento de la ejecución, no en el momento de la definición.
var data = new List<int>() {2, 4, 6, 8};
var query = data.Select(x => x * x);
Si ejecutamos la consulta en este punto con un método inmediato o foreach
, la consulta operará en la lista de números pares.
Sin embargo, si cambiamos los valores en la lista:
data.Clear();
data.AddRange(new [] {1, 3, 5, 7, 9});
O incluso si asignamos una nueva lista a los data
:
data = new List<int>() {1, 3, 5, 7, 9};
y luego ejecute la consulta, la consulta operará en el nuevo valor de los data
:
foreach (var x in query) {
Console.Write($"{x} ");
}
y dará salida a lo siguiente:
1 9 25 49 81