CodeGym /Cursos /C# SELF /Introdução a métodos anônimos (

Introdução a métodos anônimos ( delegate)

C# SELF
Nível 49 , Lição 0
Disponível

1. Introdução

Quando você programa, às vezes precisa descrever uma ação simples que é usada só uma vez e não vai servir em mais nenhum lugar. Criar um método separado para isso é como martelar um prego com um martelo de um kit de montar avião. Muito mais simples — pegar algo à mão e resolver o problema "no lugar".

Método anônimo — é um pedaço temporário de código que não precisa de nome porque é usado exatamente onde é criado. Ele:

  • É declarado dentro de outro método.
  • Não tem nome.
  • Normalmente é usado uma vez — por exemplo, passado para um delegate ou inscrito em um evento.

Hoje em dia usam mais expressões lambda (operador =>), mas entender métodos anônimos ajuda a compreender como delegates e o modelo de eventos funcionam em C#.

Quando método anônimo é conveniente?

  • Quando você precisa rapidamente passar um pedaço de lógica como parâmetro, por exemplo para ordenação, filtragem, eventos etc.
  • Não faz sentido proliferar métodos separados na classe só por lógica usada uma vez.
  • Quando você quer que o código fique compacto e fácil de ler: toda a "pequena lógica de negócio" fica num só lugar.

Um pouco de história

Pela primeira vez em C# a possibilidade de criar métodos anônimos apareceu na versão 2.0. Antes disso usar delegates era verboso: era preciso declarar um método nomeado separado, mesmo que a lógica fosse usada só num lugar. Com métodos anônimos tudo ficou mais simples, e com a chegada das expressões lambda a ideia ficou ainda mais concisa.

2. O clássico: delegates e o habitual método "nomeado"

Antes de conhecer métodos anônimos, vamos relembrar a maneira padrão de passar comportamento via delegates. Suponha que temos uma aplicação "Base de livros", onde queremos filtrar livros por diferentes condições.

// Definimos o delegate
public delegate bool BookFilter(Book book);

// Classe Book
public class Book
{
    public string Title { get; set; }
    public int Year { get; set; }
}

Antes escrevíamos assim:

// Função-filtro separada
public static bool IsClassic(Book b)
{
    return b.Year < 1970;
}

// Em algum lugar do código de uso
BookFilter filter = IsClassic;

Criar um método separado toda vez nem sempre é conveniente. E se houver dezenas de filtros?

3. Método anônimo: minimalismo, compatibilidade com delegates

Método anônimo permite definir o código do filtro exatamente onde ele é necessário:

BookFilter filter = delegate(Book b)
{
    return b.Year < 1970;
};

É isso! Sem métodos extras, tudo no lugar. delegate atua como declaração de uma função sem nome.

Sintaxe geral

delegate([argumentos])
{
    // corpo do método
};

Exemplo integrado no programa:

public class Book
{
    public string Title { get; set; }
    public int Year { get; set; }
}

public delegate bool BookFilter(Book book);

class Program
{
    static void Main()
    {
        Book[] books = {
            new Book { Title = "Mastar i Margarita", Year = 1967 },
            new Book { Title = "Clean Code", Year = 2008 }
        };

        // Filtro anônimo para clássicos
        BookFilter filter = delegate(Book b)
        {
            return b.Year < 1970;
        };

        foreach (Book book in books)
        {
            if (filter(book)) // chamamos a função anônima!
                Console.WriteLine($"{book.Title} — clássico!");
        }
    }
}

Saída:

Mastar i Margarita — clássico!

4. Método anônimo com diferentes delegates

Métodos anônimos funcionam bem com qualquer delegate. Por exemplo, os padrões Action e Func<T, TResult>, que vamos estudar mais detalhadamente depois.

Action<string> sayHello = delegate(string name)
{
    Console.WriteLine($"Olá, {name}!");
};

sayHello("Mundo"); // Olá, Mundo!

Ilustração rápida: como funciona um método anônimo "one-off"

Mais fácil imaginar um método anônimo como um trabalhador temporário: aparece para uma tarefa curta, executa — e some. Ligamos o trabalhador à tarefa via delegate.

Func<int, int, int> sum = delegate (int a, int b) {
    return a + b;
};
Console.WriteLine(sum(5, 7)); // 12

Aplicações: ordenação, busca, manipulação de coleções

Suponha que temos uma lista de livros que queremos ordenar por ano de publicação. Declarar um método separado só para a comparação é excessivo.

var books = new List<Book>
{
    new Book { Title = "Mastar i Margarita", Year = 1967 },
    new Book { Title = "Clean Code", Year = 2008 }
};

books.Sort(delegate(Book a, Book b)
{
    return a.Year.CompareTo(b.Year);
});

foreach (var book in books)
    Console.WriteLine($"{book.Title} ({book.Year})");

5. Dicas úteis

Método anônimo é a mesma coisa que expressão lambda?

No C# moderno usa-se mais expressões lambda (=>), e geralmente são bem parecidas, mas existem diferenças importantes:

  • As lambdas são mais curtas e expressivas.
  • Lambdas têm regras claras de captura de variáveis (vamos aprender isso mais adiante).
  • Métodos anônimos surgiram antes e às vezes aparecem em código legado.

Assim ficaria nosso exemplo com uma lambda:

BookFilter filter = b => b.Year < 1970;

Ainda assim, saber tanto a sintaxe antiga quanto a nova é útil para entrevistas, leitura de código alheio e entender profundamente delegates.

Método anônimo com e sem parâmetros

Se o delegate não recebe nada, dá para escrever numa linha só:

Action printHello = delegate { Console.WriteLine("Olá!"); };
printHello(); // Olá!

Se recebe parâmetros — também dá para ficar numa linha:

Action<int> printSquare = delegate (int x) { Console.WriteLine(x * x); };
printSquare(6); // 36

Método anônimo e expressão lambda

Método anônimo Expressão lambda
Sintaxe
delegate (...) {}
(args) => {}
Captura de variáveis Sim Sim
Popularidade Usado raramente Padrão
Retorno de valor Pode/obrigatório Pode/obrigatório
Multilinha Pode Pode

6. Métodos anônimos — detalhes e nuances de uso

Captura de variáveis locais
Métodos anônimos podem usar variáveis do método envolvente — esse mecanismo se chama "closure". Por exemplo:

int minYear = 1970;

BookFilter filter = delegate(Book book)
{
    return book.Year < minYear;
};

Console.WriteLine(filter(new Book { Title = "Test", Year = 1960 })); // True

Se depois você alterar minYear, o filtro vai usar o novo valor!

Não é preciso declarar parâmetros
Se não precisa de parâmetros:

Action sayHi = delegate { Console.WriteLine("Hi!"); };

Passando null
Se nenhum método foi atribuído ao delegate (por exemplo, nenhum método anônimo), o valor é null, e tentar invocar causará NullReferenceException. Tenha cuidado.

Multilinha
Um método anônimo pode conter um bloco completo de código, condicionais, loops e até outras chamadas:

Action manyThings = delegate
{
    Console.WriteLine("Começamos!");
    for (int i = 0; i < 3; i++)
        Console.WriteLine(i);
    Console.WriteLine("Terminamos!");
};
manyThings();

7. Erros típicos e particularidades

Às vezes desenvolvedores confundem o escopo das variáveis: capturar valores de um loop ou método aninhado pode levar a resultados inesperados.

List<Action> actions = new List<Action>();
for (int i = 0; i < 3; i++)
{
    actions.Add(delegate { Console.WriteLine(i); });
}
foreach (var action in actions) action(); // 3 3 3 — surpresa!

A questão é que o método anônimo "vê" a variável i — quando o loop termina, i vira 3. Todos os métodos imprimem o mesmo. Para o comportamento correto, capture a variável assim:

for (int i = 0; i < 3; i++)
{
    int current = i;
    actions.Add(delegate { Console.WriteLine(current); });
}
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION