Modos de execução do método - streaming imediato, adiado, não streaming adiado
Execução adiada vs execução imediata
Alguns métodos LINQ retornam um objeto de consulta. Este objeto não contém os resultados da consulta; em vez disso, tem todas as informações necessárias para gerar esses resultados:
var list = new List<int>() {1, 2, 3, 4, 5};
var query = list.Select(x => {
Console.Write($"{x} ");
return x;
});
A consulta contém uma chamada para Console.Write
, mas nada foi enviado para o console. Isso ocorre porque a consulta ainda não foi executada e, portanto, a função passada para Select
nunca foi avaliada. Isso é conhecido como execução adiada – a execução da consulta é atrasada até um pouco mais tarde ponto.
Outros métodos LINQ forçam uma execução imediata da consulta; esses métodos executam a consulta e geram seus valores:
var newList = query.ToList();
Neste ponto, a função passada para Select
será avaliada para cada valor na lista original, e o seguinte será enviado para o console:
1 2 3 4 5
Geralmente, os métodos LINQ que retornam um único valor (como Max
ou Count
), ou que retornam um objeto que realmente contém os valores (como ToList
ou ToDictionary
) são executados imediatamente.
Métodos que retornam um IEnumerable<T>
ou IQueryable<T>
estão retornando o objeto de consulta e permitem adiar a execução até um momento posterior.
Se um método LINQ específico força uma consulta a ser executada imediatamente ou não, pode ser encontrado em MSDN – C# ou VB. NET.
Modo de streaming (avaliação lenta) versus modo não streaming (avaliação ansiosa)
Dos métodos LINQ que usam execução adiada, alguns exigem que um único valor seja avaliado por vez. O seguinte 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}");
}
emitirá:
3 valor de iteração foreach: 3 5 valor de iteração foreach: 5 1 valor de iteração foreach: 1 2 valor de iteração foreach: 2
porque a função passada para Select
é avaliada em cada iteração do foreach
. Isso é conhecido como modo de streaming ou avaliação lenta.
Outros métodos LINQ – operadores de classificação e agrupamento – exigem que todos os valores sejam avaliados, antes que possam retornar qualquer valor:
var nonStreamingQuery = lst.OrderBy(x => {
Console.WriteLine(x);
return x;
});
foreach (var i in nonStreamingQuery) {
Console.WriteLine($"foreach iteration value: {i}");
}
emitirá:
3 5 1 2 valor de iteração foreach: 1 valor de iteração foreach: 2 valor de iteração foreach: 3 valor de iteração foreach: 5
Nesse caso, como os valores devem ser gerados para o foreach
em ordem crescente, todos os elementos devem ser avaliados primeiro, para determinar qual é o menor, qual é o próximo menor, e assim por diante. Isso é conhecido como modo não streaming ou avaliação antecipada .
Se um método LINQ específico usa o modo streaming ou não streaming, pode ser encontrado em MSDN – C# ou VB.NET .
Benefícios da execução adiada - construindo consultas
A execução adiada permite combinar diferentes operações para construir a consulta final, antes de avaliar os valores:
var list = new List<int>() {1,1,2,3,5,8};
var query = list.Select(x => x + 1);
Se executarmos a consulta neste ponto:
foreach (var x in query) {
Console.Write($"{x} ");
}
teríamos a seguinte saída:
2 2 3 4 6 9
Mas podemos modificar a consulta adicionando mais operadores:
Console.WriteLine();
query = query.Where(x => x % 2 == 0);
query = query.Select(x => x * 10);
foreach (var x in query) {
Console.Write($"{x} ");
}
Resultado:
20 20 40 60
Benefícios da execução adiada - consulta de dados atuais
Com a execução adiada, se os dados a serem consultados forem alterados, o objeto de consulta usará os dados no momento da execução, não no momento da definição.
var data = new List<int>() {2, 4, 6, 8};
var query = data.Select(x => x * x);
Se executarmos a consulta neste ponto com um método imediato ou foreach
, a consulta operará na lista de números pares.
No entanto, se alterarmos os valores na lista:
data.Clear();
data.AddRange(new [] {1, 3, 5, 7, 9});
ou mesmo se atribuirmos uma nova lista a data
:
data = new List<int>() {1, 3, 5, 7, 9};
e, em seguida, execute a consulta, a consulta operará no novo valor de data
:
foreach (var x in query) {
Console.Write($"{x} ");
}
e produzirá o seguinte:
1 9 25 49 81