C#: Expressões lambda

1. Visão geral das expressões lambda

Uma expressão Lambda é uma sintaxe concisa de função anônima que permite definir funções inline com menos código. É um recurso essencial da programação funcional em C# e a base das consultas LINQ.


2. Sintaxe de Lambda

A sintaxe básica de uma expressão Lambda é: parameters => expression or statement block. => é lido como “vai para”, mapeando os parâmetros do lado esquerdo para a operação do lado direito.

▶ Exemplo

CSHARP
using System;

class Program
{
    static void Main()
    {
        Func`<int, int>` square = x => x * x;
        Console.WriteLine(square(5));
    }
}
▶ Experimente
TEXT 📖 Somente leitura
25

3. Lambdas de uma linha e de várias linhas

Uma expressão Lambda de uma única linha retorna diretamente o resultado da expressão, sem a palavra-chave return nem chaves. Uma expressão Lambda de várias linhas requer chaves para delimitar o bloco de instruções e uma instrução explícita return.

▶ Exemplo

CSHARP
using System;

class Program
{
    static void Main()
    {
        Func`<int, int>` singleLine = x => x * x;
        Func`<int, int>` multiLine = x =>
        {
            int result = x * x;
            return result;
        };
        Console.WriteLine(singleLine(4));
        Console.WriteLine(multiLine(4));
    }
}
▶ Experimente
TEXT 📖 Somente leitura
16
16

4. Inferência de tipos

Normalmente, o compilador consegue inferir os tipos dos parâmetros Lambda a partir do contexto, eliminando a necessidade de declarações explícitas. Quando o contexto é ambíguo, é possível especificar os tipos manualmente.

▶ Exemplo

CSHARP
using System;

class Program
{
    static void Main()
    {
        Func`<int, int>` inferred = x => x + 1;
        Func`<int, int>` explicitType = (int x) => x + 1;
        Console.WriteLine(inferred(10));
        Console.WriteLine(explicitType(10));
    }
}
▶ Experimente
TEXT 📖 Somente leitura
11
11

5. Lambdas e delegados

As expressões lambda podem ser atribuídas a tipos de delegado, o que as torna a maneira mais concisa de criar instâncias de delegado. Em comparação com os métodos anônimos tradicionais ou com a instanciação explícita de delegados, as expressões lambda são mais curtas.

▶ Exemplo

CSHARP
using System;

delegate int Transform(int x);

class Program
{
    static void Main()
    {
        Transform doubleIt = x => x * 2;
        Transform addTen = x => x + 10;
        Console.WriteLine(doubleIt(3));
        Console.WriteLine(addTen(3));
    }
}
▶ Experimente
TEXT 📖 Somente leitura
6
13

6. Lambdas com Func e Action

Func<T, TResult> representa um delegado genérico com valor de retorno, e Action<T> representa um delegado genérico sem valor de retorno. É muito comum usar expressões lambda com eles.

▶ Exemplo

CSHARP
using System;

class Program
{
    static void Main()
    {
        Func`<int, int, int>` add = (a, b) => a + b;
        Action```<string>``` greet = msg => Console.WriteLine(msg);
        Console.WriteLine(add(3, 7));
        greet("Hello");
    }
}
▶ Experimente
TEXT 📖 Somente leitura
10
Hello

7. Lambdas e predicados

Predicate<T> é um delegado que retorna bool, comumente usado para filtragem e verificações condicionais. As expressões lambda tornam a definição de condições extremamente concisa.

▶ Exemplo

CSHARP
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        var numbers = new List```<int>``` { 1, 2, 3, 4, 5, 6 };
        Predicate```<int>``` isEven = x => x % 2 == 0;
        var evens = numbers.FindAll(isEven);
        Console.WriteLine(string.Join(", ", evens));
    }
}
▶ Experimente
TEXT 📖 Somente leitura
2, 4, 6

8. Fechamentos

As expressões lambda podem capturar variáveis externas, o que é chamado de closure. O tempo de vida das variáveis capturadas se estende junto com a expressão lambda — mesmo após o término do escopo original, as variáveis permanecem ativas.

▶ Exemplo

CSHARP
using System;

class Program
{
    static void Main()
    {
        int factor = 3;
        Func`<int, int>` multiply = x => x * factor;
        Console.WriteLine(multiply(5));
        factor = 10;
        Console.WriteLine(multiply(5));
    }
}
▶ Experimente
TEXT 📖 Somente leitura
15
50
💡 Um closure captura a própria variável, e não seu valor; portanto, modificar a variável externa afeta o resultado da execução do lambda.

▶ Exemplo

CSHARP
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        var actions = new List`<Action>`();
        for (int i = 0; i < 3; i++)
        {
            int copy = i;
            actions.Add(() => Console.WriteLine(copy));
        }
        foreach (var action in actions)
        {
            action();
        }
    }
}
▶ Experimente
TEXT 📖 Somente leitura
0
1
2
⚠️ Se você acessar diretamente a variável de loop i sem criar uma cópia, todas as funções lambda compartilharão a mesma i, e o resultado final será 3 em todos os casos. Criar uma cópia local evita esse problema.


9. Lambda no LINQ — Uma introdução

As expressões lambda são fundamentais para as consultas LINQ. Muitos métodos LINQ aceitam parâmetros Func ou Expression, e as expressões lambda oferecem uma maneira concisa de escrever condições, projeções e lógica de classificação diretamente no código.

▶ Exemplo

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

class Program
{
    static void Main()
    {
        var numbers = new List```<int>``` { 3, 7, 1, 9, 4, 6 };
        var big = numbers.Where(x => x > 5);
        var doubled = numbers.Select(x => x * 2);
        var sorted = numbers.OrderBy(x => x);
        Console.WriteLine(string.Join(", ", big));
        Console.WriteLine(string.Join(", ", doubled));
        Console.WriteLine(string.Join(", ", sorted));
    }
}
▶ Experimente
TEXT 📖 Somente leitura
7, 9, 6
6, 14, 2, 18, 8, 12
1, 3, 4, 6, 7, 9

10. Introdução às árvores de expressão

Quando uma função Lambda é atribuída a Expression<TDelegate>, o compilador gera uma estrutura de dados (árvore de expressões) em vez de código executável. As árvores de expressões podem ser analisadas, modificadas e transformadas em outras formas, como consultas SQL. Esse é o mecanismo subjacente a tecnologias como o LINQ to SQL.

▶ Exemplo

CSHARP
using System;
using System.Linq.Expressions;

class Program
{
    static void Main()
    {
        Expression<Func`<int, int>`> expr = x => x * x + 1;
        Console.WriteLine(expr.Body);
        Console.WriteLine(expr.Parameters[0]);
        var compiled = expr.Compile();
        Console.WriteLine(compiled(4));
    }
}
▶ Experimente
TEXT 📖 Somente leitura
(x * x) + 1
x
17

ℹ️ Expression<Func``<int,int>``> e Func<int,int> são tipos diferentes — o primeiro é uma estrutura de dados, enquanto o último é um delegado executável.

❓ Perguntas Frequentes

P: Qual é a diferença entre lambda e métodos anônimos? R: A sintaxe do lambda é mais concisa, suporta inferência de tipos e árvores de expressão; a sintaxe do método anônimo é mais prolixa e possui menos recursos. P: Quando os tipos dos parâmetros Lambda devem ser especificados explicitamente? R: Quando o compilador não consegue inferir o tipo a partir do contexto, como no caso de tipos de parâmetros ambíguos ou conflitos na resolução de sobrecargas. P: Quando as variáveis capturadas por closures são liberadas? R: As variáveis capturadas só são liberadas depois que todas as lambdas que as referenciam tiverem sido recolhidas pelo garbage collector. P: É possível converter Expression e Func um no outro? R: É possível converter Expression em Func por meio do método Compile(), mas não é possível converter Func diretamente de volta em Expression.

📖 Resumo

📝 Exercícios

  1. Escreva uma função Lambda Func<double, double, double> que calcule o máximo entre dois números
  2. Use um Lambda Action<string> para gerar uma mensagem de log com um carimbo de data e hora
  3. Escreva um exemplo de closure: crie uma função contadora que retorne um número inteiro incrementado a cada vez que for chamada
  4. Use Where com uma função Lambda para filtrar strings com mais de 5 caracteres de uma lista de strings
  5. Atribua uma função Lambda Func<int, bool> tanto a Func<int, bool> quanto a Expression<Func``<int, bool>``> e exiba seus resultados GetType() respectivamente
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%