Vamos rever um conceito básico mas importante da linguagem C# : a execução adiada |
A Execução Adiada é uma tradução ao termo Deferred Execution que significa uma execução que não foi realizada no momento mas que foi adiada para ser executada posteriormente.
Desta forma a execução adiada (ou deferred execution) refere-se a um conceito onde uma operação, especialmente em consultas LINQ (Language-Integrated Query), não é executada imediatamente quando definida.
Em vez disso, a execução da consulta ocorre apenas quando os dados são realmente
solicitados, como ao iterar sobre a coleção ou ao materializar o resultado com
métodos como .ToList()
,
.ToArray()
ou .First()
.
Nota: Quando você chama métodos como
.ToList()
,
.ToArray()
,
.First()
,
ou outros que forçam a avaliação imediata da consulta,
a execução ocorre no momento da chamada, resultando na execução
imediata.
Quando usamos consultas LINQ (Language Integrated Query), elas podem possuir dois comportamentos de execução distintos os quais são:
Execução Deferred ou Adiada
Execução Imediata
Quando a LINQ executa uma consulta ela utiliza a execução adiada ou Deferred Execution, e, isso significa que a consulta real não é executada até que os dados sejam realmente requisitados e acessados pela iteração e não quando a consulta é criada.
Vantagens da execução adiada:
- Eficiência: A execução só acontece quando necessária, evitando processamento desnecessário.
- Flexibilidade: Permite compor consultas dinamicamente antes de executá-las.
Esse é o comportamento padrão do LINQ.
Execução adiada na linguagem C#
Acontece que este comportamento não é afeto apenas à LINQ mas também é um comportamento que pode ser encontrado na linguagem C#.
Exemplos de execução adiada no C#:
Em C#, os delegates e expressões lambda permitem que uma ação seja definida, mas sua execução seja adiada até que o delegate ou a lambda seja invocada. Isso é um tipo de execução adiada, já que a ação só ocorre quando você chama explicitamente o delegate ou a lambda.
O uso de yield return
em C# cria um iterador que também utiliza execução adiada. O método que retorna
um IEnumerable<T>
usando yield
não
executa imediatamente todas as iterações. Em vez disso, os valores são gerados
conforme solicitado.
A programação assíncrona em C# com async
e await
também pode ser vista como uma forma de execução adiada. Uma tarefa (Task
)
é definida e começa a ser processada de forma assíncrona, mas seu resultado é
consumido mais tarde, quando o await
é chamado.
Assim, espera-se que desenvolvedores experientes estejam bem cientes desse recurso da plataforma .NET, mas ele pode realmente pegar de surpresa desenvolvedores mais novos.
Resumindo :
Métodos que retornam IEnumerable<T> e geram cada resultado não são executados na linha de código que realmente o chama – eles são executados quando a coleção resultante é acessada de alguma forma. Observe o motivo da maioria das instruções LINQ ter esse comportamento.
Como exemplo, veja o trecho de código abaixo:
IEnumerable<string> RepetirString5Vezes(string repetir)
{
if (repetir == null)
throw new ArgumentNullException(nameof(repetir));
for (int i = 0; i < 5; i++)
{
if (i == 3)
throw new InvalidOperationException("O número 3 foi alcançado...");
yield return $"{repetir} - {i}";
}
}
|
Qual será o resultado obtido para o código a seguir:
var resultado1 = RepetirString5Vezes(null); |
Este código não será executado pois resultado1 não esta sendo usado, logo o corpo do método nunca será chamado.
Isso ocorre por que RepetirString5Vezes() retorna um IEnumerable<string> e somente seria executado quando a coleção fosse acessada, mas como resultado1 não é usado isso nunca vai ocorrer.
E agora qual o resultado obtido para o código abaixo:
|
Este código também vai se comportar da seguinte forma:
- O método
RepetirString5Vezes() será chamada passando o parâmetro 'teste';
- E o valor em resultado2.First() vai ser recuperado;
- No entanto a exceção InvalidOperationException nunca será lançada, ou seja i==3 nunca será verdadeiro. Assim se formos testar para obter a exceção o teste iria falhar;
Este recurso é largamente usado nas consultas LINQ.
Assim nunca esqueça que na execução adiada a avaliação de uma expressão é atrasada até que seu valor realizado seja realmente
Isso vai melhorar muito o desempenho da sua aplicação pois evita a execução desnecessária.
E estamos conversados.
"Celebrai com júbilo ao SENHOR, todas as terras.
Servi ao Senhor com
alegria; e entrai diante dele com canto. "
Salmos 101:1-2
Referências:
C# - Lendo e escrevendo em arquivos textos e binários
C# - Entendo o I/O na plataforma .NET
C# - Fluxo assíncrono ou async streams
C#- Apresentando Streams assíncronos