0Pricing
C# Academy · Aula

Semântica da avaliação tardia

Entenda a execução adiada dos iteradores.

Semântica da avaliação tardia é uma aula grátis de C# Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de C# Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de C# Academy inclui 4 aulas no total.

Execução adiada

Os iteradores e a query do LINQ usam execução adiada: definir a query quase não faz nada. O trabalho só acontece quando você realmente enumera o resultado, item por item.

Nada é executado até enumerar

Chamar um método iterador não executa o seu corpo. O corpo começa a ser executado somente quando um foreach (ou outro consumidor) solicita o primeiro valor.

using System;
using System.Collections.Generic;

public class Program
{
    static IEnumerable<int> Numbers()
    {
        Console.WriteLine("-- iterator body started --");
        yield return 1;
        yield return 2;
    }

    public static void Main()
    {
        var seq = Numbers();
        Console.WriteLine("query created, body not run yet");
        foreach (var n in seq) Console.WriteLine(n);
    }
}

Os valores são produzidos um de cada vez

Cada iteração executa apenas o suficiente do iterador para produzir o próximo valor. Esse modelo baseado em solicitações mantém o uso de memória baixo, mesmo para sequências enormes.

using System;
using System.Collections.Generic;

public class Program
{
    static IEnumerable<int> Trace()
    {
        for (int i = 1; i <= 3; i++)
        {
            Console.WriteLine("producing " + i);
            yield return i;
        }
    }

    public static void Main()
    {
        foreach (var n in Trace())
            Console.WriteLine("consumed " + n);
    }
}

Reenumerar executa novamente

Uma sequência adiada não tem um resultado armazenado em cache. Enumerá-la duas vezes executa o iterador duas vezes, o que é importante se a origem mudar ou se o trabalho for caro.

using System;
using System.Collections.Generic;

public class Program
{
    static int _calls = 0;
    static IEnumerable<int> Counter()
    {
        _calls++;
        yield return _calls;
    }

    public static void Main()
    {
        var seq = Counter();
        foreach (var n in seq) Console.WriteLine(n);
        foreach (var n in seq) Console.WriteLine(n);
        Console.WriteLine("total runs: " + _calls);
    }
}

Capturando o estado atual

Como a execução é adiada, um iterador lê sua origem no momento da enumeração, não no momento da definição. As alterações feitas depois da definição da query ficam visíveis.

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        var list = new List<int> { 1, 2 };
        IEnumerable<int> doubled = Double(list);
        list.Add(3); // added before enumeration
        foreach (var n in doubled) Console.WriteLine(n);
    }

    static IEnumerable<int> Double(IEnumerable<int> nums)
    {
        foreach (var n in nums) yield return n * 2;
    }
}

Forçando a execução imediata

Para criar um instantâneo dos resultados, materialize a sequência com ToList() ou ToArray(). Isso executa o iterador uma vez e armazena os valores, desvinculando-os de alterações posteriores na origem.

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    public static void Main()
    {
        var list = new List<int> { 1, 2 };
        List<int> snapshot = Double(list).ToList(); // executed now
        list.Add(99);
        Console.WriteLine(string.Join(",", snapshot));
    }

    static IEnumerable<int> Double(IEnumerable<int> nums)
    {
        foreach (var n in nums) yield return n * 2;
    }
}

A interrupção antecipada economiza trabalho

Como os consumidores solicitam valores de forma preguiçosa, uma operação como first pode parar cedo. O produtor nunca calcula valores que não são solicitados.

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    static IEnumerable<int> Numbers()
    {
        for (int i = 1; ; i++)
        {
            Console.WriteLine("checking " + i);
            yield return i;
        }
    }

    public static void Main()
    {
        int firstBig = Numbers().First(n => n > 3);
        Console.WriteLine("found " + firstBig);
    }
}

Exceções adiadas

Uma exceção dentro de um iterador não é lançada quando o método é chamado, mas quando a enumeração chega à linha que falha. Isso pode surpreendê-lo se você esperar uma validação antecipada.

using System;
using System.Collections.Generic;

public class Program
{
    static IEnumerable<int> Risky(int divisor)
    {
        yield return 10 / divisor; // throws only during enumeration
    }

    public static void Main()
    {
        var seq = Risky(0);
        Console.WriteLine("no error yet");
        try { foreach (var n in seq) Console.WriteLine(n); }
        catch (DivideByZeroException) { Console.WriteLine("caught during enumeration"); }
    }
}

Composição eficiente de fluxos preguiçosos

Encadear operadores preguiçosos não os executa repetidamente. Cada valor percorre todo o fluxo uma vez, conforme necessário, o que é eficiente e econômico em memória.

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    public static void Main()
    {
        var result = Enumerable.Range(1, 1000000)
            .Where(n => n % 2 == 0)
            .Select(n => n * n)
            .Take(3);
        foreach (var n in result) Console.WriteLine(n);
    }
}

Quando materializar

Materialize quando for enumerar várias vezes, quando a origem puder mudar ou quando precisar capturar os resultados antes de liberar um recurso. Caso contrário, mantenha a execução preguiçosa para economizar memória e trabalho.

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    public static void Main()
    {
        var evens = Enumerable.Range(1, 10).Where(n => n % 2 == 0).ToList();
        // Safe to enumerate many times now
        Console.WriteLine("count: " + evens.Count);
        Console.WriteLine("sum: " + evens.Sum());
    }
}

Experimente por si mesmo

Observe diretamente a execução adiada: uma query é definida, a origem é alterada e as mudanças só aparecem nos resultados no momento da enumeração.

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    public static void Main()
    {
        var source = new List<int> { 1, 2, 3 };
        var query = source.Where(n => n > 1); // not executed yet
        source.Add(10);
        source.Remove(2);
        // Executes now, reflecting all changes
        Console.WriteLine(string.Join(", ", query));
    }
}

Verificação rápida

Raciocine sobre a execução adiada.

Recapitulação

A avaliação preguiçosa (adiada) executa uma sequência somente quando ela é enumerada.

  • Definir uma query não faz nenhum trabalho; consumi-la faz.
  • Reenumerar executa o iterador novamente.
  • As origens são lidas no momento da enumeração, portanto alterações posteriores ficam visíveis.
  • As exceções aparecem durante a enumeração.
  • Use ToList()/ToArray() para materializar quando necessário.

Perguntas Frequentes

A aula “Semântica da avaliação tardia” é grátis?

Sim — o texto completo de “Semântica da avaliação tardia” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de C# Academy, atualize para CoddyKit PRO. O curso de C# Academy inclui 4 aulas no total.

O que vou aprender em “Semântica da avaliação tardia”?

Entenda a execução adiada dos iteradores. Você pratica C# Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar C# Academy?

Nenhuma experiência prévia é necessária. C# Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.

Quanto tempo leva a aula “Semântica da avaliação tardia”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de C# Academy?

Sim. Cada aula de C# Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Métodos iteradores com yield return
  2. yield break e encerramento antecipado
  3. Semântica da avaliação tardia
  4. Enumeráveis personalizados
← Voltar para C# Academy