1. Introdução
Em C# (e no .NET em geral) eventos são uma das maneiras pelas quais objetos se comunicam entre si. Por exemplo, você tem um botão cujo clique deve disparar algo. Poderíamos implementar o tratamento de eventos declarando um método separado e então assinando explicitamente esse método ao evento... Mas muitas vezes queremos simplesmente "descrever a reação aqui mesmo", sem espalhar a lógica por várias partes do arquivo.
Expressões lambda resolvem isso muito bem — permitem literalmente escrever a reação ao evento no mesmo lugar onde você se inscreve para ele. Mínimo de cerimônia — máximo de utilidade.
O mesmo vale para callbacks: às vezes você passa para um método alguma lógica para executar "depois", por exemplo, quando uma operação terminar — e de novo, expressões lambda fazem isso de forma simples e clara: toda a lógica necessária está ali, no ponto da chamada.
Eventos e callbacks: o essencial e onde as lambdas são úteis
Evento — nada mais do que uma "placa" no objeto: "aqui alguém pode se inscrever e fazer algo quando algo acontecer".
Callback (chamada de retorno) — é "eu te dou uma função agora, e você a chama quando precisar". Muito parecido com "deixe seu pedido — nós retornamos a ligação".
Quando você aprendeu sobre delegates e eventos antes, já viu uma sintaxe mais ou menos assim:
button.Click += MyButtonClickHandler;
Onde MyButtonClickHandler é um método especial que precisa ser declarado em algum lugar. Mas quando a lógica é simples, você prefere evitar criar métodos só para uma ação.
É aí que as lambdas entram em cena:
button.Click += (sender, args) => { Console.WriteLine("Botão clicado!"); };
Conciso, claro, aqui mesmo!
2. Lambdas como handlers de eventos: do simples ao interessante
Forma simples: a lâmpada piscou — escrevemos algo no console
Suponha que temos uma classe Button, e queremos reagir ao seu evento Click.
// Imagine um botão assim:
public class Button
{
public event EventHandler? Click;
public void SimulateClick() // Para exemplo: "clicamos" programaticamente
{
// Se alguém estiver inscrito — chamamos os handlers
Click?.Invoke(this, EventArgs.Empty);
}
}
Agora usamos esse botão:
var button = new Button();
// Assinamos o evento do botão usando uma lambda!
button.Click += (sender, args) =>
{
Console.WriteLine("Oba! O botão foi clicado!");
};
button.SimulateClick(); // => Oba! O botão foi clicado!
Explicação:
— Tudo que vem depois de => é o corpo da sua "mini-função", que será chamada quando o evento ocorrer.
— Não precisa se preocupar com o ciclo de vida do handler: ele vive enquanto sua assinatura existir (+=). Importante: se a lambda captura variáveis externas e você não se desinscrever do evento, isso pode causar vazamento de memória.
Handler lambda com captura de variáveis
int clickCount = 0;
button.Click += (sender, args) =>
{
clickCount++;
Console.WriteLine($"O botão foi clicado já {clickCount} vez(es)!");
};
button.SimulateClick(); // => O botão foi clicado já 1 vez(es)!
button.SimulateClick(); // => O botão foi clicado já 2 vez(es)!
O que acontece:
A lambda "captura" a variável clickCount do escopo externo e lembra seu valor entre as chamadas.
3. Uso de expressões lambda para callbacks
Callback é frequentemente usado quando algum código precisa chamar sua função mais tarde — por exemplo, depois de realizar um trabalho demorado.
Passando uma lambda como callback
Suponha que temos um método que aceita um delegate ou lambda como argumento:
void DoWork(Action callback)
{
Console.WriteLine("O trabalho está começando...");
// Simulação de trabalho
System.Threading.Thread.Sleep(500); // (Não faça isso em apps reais, é uma operação bloqueante!)
callback(); // Depois do trabalho chamamos o callback
}
DoWork(() => Console.WriteLine("Trabalho concluído!"));
Resultado:
Primeiro no console aparecerá "O trabalho está começando...", depois "Trabalho concluído!".
Você pode passar lambdas com parâmetros:
void Calculate(int a, int b, Action<int> onResult)
{
int sum = a + b;
onResult(sum);
}
Calculate(5, 8, result => Console.WriteLine($"Resultado: {result}"));
4. Erros típicos ao usar lambdas
Não se desinscrever de eventos
Quando você assina um evento com uma lambda, você não tem um "nome do handler" para depois se desinscrever facilmente:
button.Click += (s, e) => Console.WriteLine("...");
button.Click -= ??? // Como passar sua lambda aqui? Impossível, se ela não foi salva.
Se realmente precisa se desinscrever — guarde a lambda numa variável:
EventHandler handler = (s, e) => Console.WriteLine("...");
button.Click += handler;
// ...depois
button.Click -= handler;
Isso é importante para objetos de longa vida: se não se desinscrever, a referência vai ficar e possivelmente causar vazamento de memória.
Captura de variáveis em loop
Clássico: assinar eventos dentro de um loop:
for (int i = 0; i < 3; i++)
button.Click += (s, e) => Console.WriteLine(i); // Todas as lambdas "lembram" o mesmo i
Em versões modernas do C# esse comportamento foi corrigido para foreach, mas para for às vezes há nuances — fique sempre atento com variáveis capturadas!
Se precisar que cada lambda lembre seu próprio valor — crie uma cópia local:
for (int i = 0; i < 3; i++)
{
int localI = i;
button.Click += (s, e) => Console.WriteLine(localI);
}
GO TO FULL VERSION