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 | |
|
| 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); });
}
GO TO FULL VERSION