Yield return em C#: magia preguiçosa para iniciantes

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Yield return em C#: quando o código funciona "de forma preguiçosa" e isso é legal

\n\n

Imagine que você está escrevendo um programa que precisa processar um milhão de linhas de um arquivo de log. Você honestamente carrega tudo em uma lista List<string>, passa para um método de filtragem, e então — puf! — o programa trava, a memória vaza, e o usuário nervosamente aperta Ctrl+Alt+Del.

\n\n

Familiar? Se você já enfrentou essa situação pelo menos uma vez, então está na hora de conhecer a "magia preguiçosa" do C# — o operador yield return. Isso não é apenas açúcar sintático, mas uma ferramenta poderosa que pode mudar radicalmente a forma como você trabalha com coleções.

\n\n

Neste artigo, vamos analisar o uso correto do yield return com exemplos reais, explorar os bastidores e descobrir quando usá-lo, e quando é melhor evitar. Vamos lá!

\n\n

O que é yield return e como funciona?

\n\n

Explicando de forma simples, yield return é uma forma de dizer ao compilador: "Ei, eu não quero retornar a coleção inteira de uma vez. Vou entregar os elementos um por um, conforme necessário". Essa abordagem é chamada de computação preguiçosa (lazy evaluation).

\n\n

Aqui está um exemplo clássico:

\n\n
public IEnumerable<int> GetNumbers()\n{\n    yield return 1;\n    yield return 2;\n    yield return 3;\n}
\n\n

Ao chamar este método, você não obtém um array [1, 2, 3]. Em vez disso, você obtém um iterador que entregará os números um por um, quando solicitado. Cada chamada a MoveNext() avança a execução do método até o próximo yield return.

\n\n

Isso é como uma esteira: os elementos são produzidos apenas quando o consumidor precisa deles. Sem cópias desnecessárias, sem listas gigantescas na memória — apenas trabalho limpo e eficiente.

\n\n

Por que yield return não é apenas "retornar uma lista"?

\n\n

Muitos iniciantes pensam: "Por que eu usaria yield return se posso simplesmente criar um List e retorná-lo?". Vamos analisar com um exemplo concreto.

\n\n

Imagine que você precisa obter os primeiros 10 números pares de uma sequência infinita. Com yield return, isso é feito de forma elegante:

\n\n
public IEnumerable<int> GetEvenNumbers()\n{\n    int i = 0;\n    while (true)\n    {\n        yield return i;\n        i += 2;\n    }\n}\n\n// Uso\nvar firstTenEven = GetEvenNumbers().Take(10).ToList();
\n\n

Tente fazer o mesmo com List<int> — ou você entrará em um loop infinito, ou terá que armazenar todos os números gerados na memória. Yield return nos dá a capacidade de trabalhar com sequências potencialmente infinitas sem sobrecarregar a memória RAM.

\n\n

Exemplo prático: leitura de um arquivo grande

\n\n

Voltemos ao nosso arquivo de log de um milhão de linhas. Veja como os iniciantes geralmente escrevem:

\n\n
public List<string> ReadLogLines(string path)\n{\n    var lines = new List<string>();\n    using (var reader = new StreamReader(path))\n    {\n        string line;\n        while ((line = reader.ReadLine()) != null)\n        {\n            lines.Add(line);\n        }\n    }\n    return lines;\n}
\n\n

Problema: o arquivo inteiro é carregado na memória. Se o arquivo pesa 500 MB, a lista pesará aproximadamente o mesmo. E se você só precisa filtrar as linhas com erros? Mesmo assim, você carregará tudo.

\n\n

Agora, o uso correto de yield return:

\n\n
public IEnumerable<string> ReadLogLines(string path)\n{\n    using (var reader = new StreamReader(path))\n    {\n        string line;\n        while ((line = reader.ReadLine()) != null)\n        {\n            yield return line;\n        }\n    }\n}\n\n// Filtragem sem carregar o arquivo inteiro\nvar errorLines = ReadLogLines("log.txt")\n    .Where(line => line.Contains("ERROR"))\n    .Take(100);<                

Blogs

Book Recommendations