1. Introdução
Você talvez não soubesse, mas uma das perguntas favoritas em entrevistas é — “O que você sabe sobre expressões lambda?”. Por que perguntam isso? Porque é uma coisa muito prática e "na moda" em C#, que deixa o código menor e mais expressivo. Se métodos anônimos são como escrever uma carta à mão, as expressões lambda são como mandar uma mensagem no mensageiro: rápido, compacto e às vezes até com emojis (bom, quase).
As expressões lambda chegaram no C# na versão 3.0 e desde então se espalharam por todo lado: em LINQ, handlers de eventos, threads, coleções e até em bibliotecas de IA no .NET.
Expressão lambda — é uma forma de declarar um método diretamente no ponto do código, "in-loco", sem nome, supercurta e clara. A sintaxe principal sempre fica assim:
(parâmetros) => expressão_ou_bloco_de_código
Esse "chevron" => é chamado de “operador lambda” ou “seta”.
Uma pequena analogia
Lembre-se de como você escreve uma receita: “Pegar a maçã, cortar ela, colocar na tigela”.
Em C# isso seria:
(maçã) => cortar(maçã)
2. Transformando um método anônimo em lambda
Suponha que temos um delegate:
delegate int SquareDelegate(int x);
Um método anônimo ficaria assim:
SquareDelegate sq = delegate(int x) {
return x * x;
};
E agora a mesma coisa com expressão lambda:
SquareDelegate sq = (int x) => { return x * x; };
Mas o C# consegue inferir os tipos, e podemos encurtar ainda mais:
SquareDelegate sq = x => x * x;
Impressionante compactação!
Comparação com método "comum" e função anônima
| Maneira | Declaração | Onde pode ser usado | Contras |
|---|---|---|---|
| Método nomeado | Na classe | Em qualquer lugar | Precisa de nome, mais código |
| Método anônimo | No código | Apenas com delegates | Sintaxe mais verbosa |
| Expressão lambda | No código | Em qualquer lugar com delegates | Às vezes o tipo não é óbvio |
3. Sintaxe de expressão lambda: variações
Parâmetros
Sem parâmetros:
Action hello = () => Console.WriteLine("Olá, mundo!");
Um parâmetro (pode omitir parênteses):
Func<int, int> inc = x => x + 1;
Vários parâmetros (precisa parênteses):
Func<int, int, int> sum = (a, b) => a + b;
Corpo da lambda
Uma expressão — sem chaves e sem return:
x => x * x
Bloco de código — chaves, é preciso return:
(x, y) =>
{
int z = x + y;
return z * z;
}
Tipos dos parâmetros
Na maioria das vezes não precisa especificar o tipo — o compilador infere. Mas se quiser, pode:
(x, y) => x + y // O compilador infere os tipos.
(int x, int y) => x + y // Pode declarar explicitamente.
4. Nuances e exemplos mais reais
Uso com coleções
Lembre do nosso miniprojeto didático (por exemplo, lista de usuários)? Suponha que temos um array de números:
int[] numbers = { 1, 2, 3, 4, 5 };
Precisamos selecionar apenas os números pares:
var evenNumbers = numbers.Where(n => n % 2 == 0);
Aqui Where é um método de extensão do LINQ, e a condição é nosso filtro lambda.
Passando para um método
Suponha que temos um método-delegate:
public delegate bool Filter(int number);
public static int[] FilterNumbers(int[] data, Filter predicate)
{
var result = new List<int>();
foreach (var n in data)
if (predicate(n))
result.Add(n);
return result.ToArray();
}
Agora passamos uma expressão lambda:
int[] evens = FilterNumbers(numbers, n => n % 2 == 0);
Lambda como handler de evento
button.Click += (sender, args) => Console.WriteLine("Botão pressionado!");
5. Lambdas e delegados genéricos padrão
É difícil imaginar lambdas sem Func<>, Action<> e Predicate<>. São tipos especiais de delegates:
- Action — não retorna nada.
- Func — retorna um valor.
- Predicate — retorna bool, normalmente para filtragem.
Exemplo com Action:
Action<string> log = message => Console.WriteLine(message);
log("Esta mensagem via lambda!");
Exemplo com Func:
Func<int, int, int> multiply = (a, b) => a * b;
int product = multiply(3, 5); // 15
Exemplo com Predicate:
Predicate<int> isNegative = n => n < 0;
bool test = isNegative(-7); // true
6. Lambdas com múltiplas expressões: quando usar bloco
Se a lambda precisa fazer várias operações, use chaves e o return explícito:
Func<int, int, string> describeSum = (a, b) =>
{
int sum = a + b;
return $"Soma: {sum}";
};
Sem return o compilador vai reclamar (mas nem sempre explica bem o motivo — geralmente diz apenas que "nem todos os caminhos retornam um valor").
7. Retorno void: Action
Quando a lambda não retorna nada, use Action:
Action greet = () => Console.WriteLine("Sorria — o código compila!");
Uma lambda pode conter quantas instruções quiser:
Action<int> printSquare = x =>
{
int sq = x * x;
Console.WriteLine($"O quadrado de {x} é {sq}");
};
8. Restrições e erros típicos
Às vezes dá vontade de escrever a lambda bem solta, mas o compilador nem sempre concorda.
Não dá para declarar uma variável com o mesmo nome de uma que foi capturada de fora.
Fique atento aos tipos: se a assinatura do delegate e da lambda não baterem — erro na certa.
O retorno é obrigatório se o delegate exige um valor de retorno.
Em lambdas complexas não esqueça das chaves. Uma expressão — sem chaves e sem return. Vários statements — chaves e return.
GO TO FULL VERSION