CodeGym /Cursos /C# SELF /Passando expressões lambda ( =>) como parâmetro

Passando expressões lambda ( =>) como parâmetro

C# SELF
Nível 50, Lição 2
Disponível

1. Introdução

Até agora você viu: chamávamos métodos do LINQ que recebiam uma lambda. Mas como o método em C# sabe que dá pra passar uma função pra ele?

De forma simples, delegates são como uma interface para funções: descrevemos a assinatura (tipos dos parâmetros e tipo de retorno), e qualquer método (ou lambda!) que combinar com essa assinatura pode ser passado onde esse delegate é esperado.

Lembra como você passava uma string como parâmetro? Funciona mais ou menos do mesmo jeito com “lógica”, só que o tipo do parâmetro é um delegate.

Delegates: teoria básica numa linguagem simples

Em C# delegate é um tipo que descreve “uma função com determinada assinatura”.

// Delegate que recebe int e retorna bool
public delegate bool IntPredicate(int x);

Qualquer função compatível com a assinatura pode ser atribuída a uma variável desse tipo:

bool IsEven(int n) => n % 2 == 0;

IntPredicate pred = IsEven;

E agora — uma lambda também serve:

IntPredicate pred = x => x % 2 == 0;

Delegates genéricos: Func, Action, Predicate

  • Func<T1, ..., TResult> — função que recebe parâmetros T1, ... e retorna TResult.
  • Action<T1, ...> — função que recebe parâmetros e não retorna valor (void).
  • Predicate<T> — função que recebe T e retorna bool.

2. Passando uma lambda para seu método

Vamos imaginar que estamos expandindo nosso mini-app de estudo — um projeto console que trabalha com uma lista de usuários. Antes a gente filtrava coleções via LINQ, agora vamos escrever nosso próprio método que recebe uma condição em forma de lambda.

Criando um método com parâmetro lambda

// Definimos a classe User como exemplo (adicionar ao app)
public class User
{
    public string Name { get; set; }
    public bool IsActive { get; set; }
}

// Método que recebe a lista e um delegate-condição (lambda)
public static List<User> FilterUsers(List<User> users, Predicate<User> predicate)
{
    var result = new List<User>();
    foreach (var user in users)
    {
        if (predicate(user)) // Chamamos a lambda!
            result.Add(user);
    }
    return result;
}

Agora dá pra passar qualquer lambda:

var users = new List<User>
{
    new User { Name = "Vasya", IsActive = true },
    new User { Name = "Petya", IsActive = false },
    new User { Name = "Masha", IsActive = true }
};

// Filtramos apenas os ativos
var activeUsers = FilterUsers(users, user => user.IsActive);

foreach (var user in activeUsers)
    Console.WriteLine(user.Name); // Vasya, Masha

É isso! Passamos um pedaço de lógica — uma mini-função — como parâmetro normal, só porque o método FilterUsers espera um Predicate<User>, e nós demos uma lambda compatível.

Variação com Func<T, TResult>

Predicate<T> serve quando você precisa de uma condição (retorna bool). E se quisermos “calcular” algo para cada usuário?

// Método que aplica uma função a cada elemento e coleta resultados
public static List<TResult> MapUsers<TResult>(List<User> users, Func<User, TResult> selector)
{
    var result = new List<TResult>();
    foreach (var user in users)
    {
        result.Add(selector(user));
    }
    return result;
}

Uso:

var names = MapUsers(users, user => user.Name.ToUpper());

foreach (var name in names)
    Console.WriteLine(name); // VASYA, PETYA, MASHA

3. Dicas úteis

Diferentes formas de passar

Você pode passar não só uma lambda, mas também um método normal — o importante é que a assinatura bata.

// Método normal
static bool NameHasS(User user) => user.Name.Contains("s");

// Passando o método normal:
var usersWithS = FilterUsers(users, NameHasS);
// Passando uma lambda
var usersWithA = FilterUsers(users, u => u.Name.Contains("a"));

Também dá pra usar um método anônimo antigo (mas não recomendo):

var usersWithM = FilterUsers(users, delegate(User u) { return u.Name.Contains("m"); });

Estilo moderno = lambdas!

Passando lambdas para LINQ: o que realmente acontece

var result = users.Where(u => u.IsActive).ToList();

Por baixo do capô o Where aceita um Func<User, bool>. Isso quer dizer que qualquer método que aceite Func<...> pode ser usado da mesma forma!

E se precisar de dois parâmetros?

// Método aceita duas lambdas pra filtragem
public static List<User> FilterUsersCustom(
    List<User> users,
    Func<User, bool> include,
    Func<User, bool> exclude)
{
    var result = new List<User>();
    foreach (var user in users)
    {
        if (include(user) && !exclude(user))
            result.Add(user);
    }
    return result;
}

Uso:

var customFiltered = FilterUsersCustom(
    users,
    u => u.Name.StartsWith("V"),
    u => u.IsActive == false
);
// Vai pegar só usuários cujos nomes começam com "V" e que são ativos

Cenário: Fábrica de filtros

Console.WriteLine("Digite o comprimento mínimo do nome:");
int minLength = int.Parse(Console.ReadLine());

Predicate<User> lengthFilter = user => user.Name.Length >= minLength;
var filteredUsers = FilterUsers(users, lengthFilter);

// Bem interativo e dinâmico!

4. Erros típicos e sutilezas

Às vezes o compilador não consegue "inferir" os tipos dos parâmetros da lambda — especialmente em cenários complexos com overloads ou quando o método exige um delegate com vários parâmetros/tipo de retorno específico. Nesse caso você pode especificar explicitamente os tipos na lambda:

FilterUsers(users, (User u) => u.Name.Length > 3);

ou mesmo:

MapUsers(users, (User u) => u.Name.ToUpper());

Erro: lambda não corresponde à assinatura

FilterUsers(users, user => Console.WriteLine(user.Name)); // erro!  Esperava-se bool, recebeu-se void

Isso acontece porque se espera uma função que retorne bool, mas a lambda retorna void (na verdade, não retorna nada). Preste atenção ao tipo de retorno!

Erro: abuso de lambdas

Se você começa a passar lambdas de 10 linhas, melhor extrair pra um método separado. Fica mais legível e mais fácil de debugar.

2
Tarefa
C# SELF, nível 50, lição 2
Bloqueado
Filtragem avançada e transformação de usuários
Filtragem avançada e transformação de usuários
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION