Uso Eficaz de Delegates para Implementação de Callbacks em C#
Introdução aos Delegates em C#
Delegates em C# são tipos que representam referências a métodos com um tipo de assinatura específico. Eles permitem que você encapsule um método em um objeto, o que é particularmente útil para implementar callbacks e eventos. Um delegate pode ser considerado como um ponteiro de função, permitindo que você passe métodos como parâmetros para outros métodos (TUTORIALSPOINT, 2025).
A definição de um delegate é feita utilizando a palavra-chave delegate
, seguida pela assinatura do método que ele irá referenciar. Vamos considerar um exemplo simples de um delegate que representa um método que recebe dois inteiros e retorna um inteiro.
public delegate int Operacao(int x, int y);
public class Calculadora
{
public int Somar(int x, int y) => x + y;
public int Subtrair(int x, int y) => x - y;
}
Implementação de Callbacks com Delegates
Callbacks são uma técnica onde um método é passado como argumento para outro método. Isso é útil em situações onde você deseja que um método execute uma operação específica após completar uma tarefa. Com delegates, a implementação de callbacks se torna mais simplificada e organizada (IDC ONLINE, 2025).
No exemplo abaixo, vamos criar um método que aceita um delegate como argumento, permitindo que o usuário escolha a operação a ser realizada após a execução de uma tarefa.
public void ExecutarOperacao(Operacao operacao, int a, int b)
{
int resultado = operacao(a, b);
Console.WriteLine($"Resultado: {resultado}");
}
// Uso
var calculadora = new Calculadora();
ExecutarOperacao(calculadora.Somar, 5, 3); // Resultado: 8
ExecutarOperacao(calculadora.Subtrair, 5, 3); // Resultado: 2
Delegates Multicast
Delegates multicast permitem que você associe múltiplos métodos a um único delegate. Quando o delegate é invocado, todos os métodos associados são executados em sequência. Isso é especialmente útil em eventos, onde você pode ter múltiplos manipuladores de eventos (IDC ONLINE, 2025).
public delegate void Notificacao(string mensagem);
public class Notificador
{
public Notificacao OnNotificacao;
public void EnviarNotificacao(string mensagem)
{
OnNotificacao?.Invoke(mensagem);
}
}
// Uso
var notificador = new Notificador();
notificador.OnNotificacao += msg => Console.WriteLine($"Recebido: {msg}");
notificador.OnNotificacao += msg => Console.WriteLine($"Log: {msg}");
notificador.EnviarNotificacao("Nova mensagem!"); // Recebido: Nova mensagem! Log: Nova mensagem!
Delegates e LINQ
O uso de delegates se estende ao LINQ (Language Integrated Query), onde você pode passar expressões lambda como delegates. Isso permite que você escreva consultas de forma concisa e expressiva.
using System;
using System.Collections.Generic;
using System.Linq;
var numeros = new List { 1, 2, 3, 4, 5 };
var quadrados = numeros.Select(n => n * n).ToList();
Console.WriteLine(string.Join(", ", quadrados)); // Saída: 1, 4, 9, 16, 25
Delegates Genéricos
Em C#, também é possível criar delegates genéricos, que permitem a definição de um delegate que pode operar em diferentes tipos de dados. Isso aumenta a flexibilidade e reutilização do código.
public delegate T ResultadoGenerico(T x, T y);
public class Exemplo
{
public T Calcular(ResultadoGenerico operacao, T a, T b)
{
return operacao(a, b);
}
}
// Uso
var exemplo = new Exemplo();
var soma = exemplo.Calcular((x, y) => x + y, 3, 4); // 7
var concat = exemplo.Calcular((x, y) => x + y, "Olá, ", "mundo!"); // Olá, mundo!
Eventos e Delegates
Eventos em C# são uma forma de encapsular a lógica de delegação, permitindo que classes publiquem notificações sobre mudanças de estado. Eventos são baseados em delegates e normalmente são declarados como delegates públicos.
public class Contador
{
private int _contagem;
public event Notificacao ContagemMudou;
public void Incrementar()
{
_contagem++;
ContagemMudou?.Invoke($"Contagem atual: {_contagem}");
}
}
// Uso
var contador = new Contador();
contador.ContagemMudou += msg => Console.WriteLine(msg);
contador.Incrementar(); // Contagem atual: 1
contador.Incrementar(); // Contagem atual: 2
Considerações sobre Performance
Embora o uso de delegates e callbacks em C# seja extremamente útil, é importante considerar o impacto na performance, especialmente em aplicações críticas. Delegates podem introduzir uma sobrecarga devido à criação de objetos e à invocação de métodos. Portanto, é aconselhável usar delegates com sabedoria e considerar alternativas quando necessário.
Exemplo Prático: Sistema de Notificação de Eventos
Vamos explorar um exemplo prático de um sistema de notificação de eventos utilizando delegates. Neste sistema, uma classe Evento
irá disparar notificações quando um determinado evento ocorrer, como a conclusão de uma tarefa.
public class Evento
{
public event Notificacao NotificacaoEvento;
public void TarefaConcluida()
{
NotificacaoEvento?.Invoke("A tarefa foi concluída com sucesso!");
}
}
// Uso
var evento = new Evento();
evento.NotificacaoEvento += msg => Console.WriteLine($"Notificação: {msg}");
evento.TarefaConcluida(); // Notificação: A tarefa foi concluída com sucesso!
Delegates e Programação Assíncrona
Os delegates também se mostram extremamente úteis em contextos de programação assíncrona, onde você pode querer executar métodos após a conclusão de uma operação longa sem bloquear a interface do usuário. O uso de delegates com async
e await
facilita essa implementação.
public async Task ProcessarDadosAsync()
{
await Task.Run(() =>
{
// Simula um processamento demorado
Thread.Sleep(2000);
});
NotificacaoEvento?.Invoke("Processamento concluído.");
}
// Uso
public async Task Executar()
{
Evento evento = new Evento();
evento.NotificacaoEvento += msg => Console.WriteLine(msg);
await evento.ProcessarDadosAsync(); // Processamento concluído.
}
Delegates como Parâmetros de Métodos
Outra aplicação importante dos delegates é passá-los como parâmetros para métodos. Isso aumenta a flexibilidade dos métodos, pois você pode alterar o comportamento deles sem modificar seu código interno (WAGNER, 2011).
public void ExecutarComCallback(Action callback)
{
// Lógica do método principal
Console.WriteLine("Executando lógica principal...");
// Chama o callback após a execução
callback();
}
// Uso
ExecutarComCallback(() => Console.WriteLine("Callback executado!"));
Comparação entre Delegates e Interfaces
Muitas vezes, pode haver confusão entre o uso de delegates e interfaces para implementar callbacks. Enquanto os delegates são mais simples e diretos, as interfaces oferecem uma estrutura mais robusta e podem ser usadas para implementar herança múltipla. A escolha entre um e outro depende do contexto da aplicação.
Conclusão
Delegates são uma ferramenta poderosa em C# que permite a implementação eficiente de callbacks e eventos. Com os exemplos apresentados, você deve ter uma melhor compreensão de como usar delegates em suas aplicações. Desde a implementação básica até o uso em LINQ e eventos, os delegates oferecem flexibilidade e expressividade ao seu código. Ao utilizar essa funcionalidade, lembre-se sempre de considerar a performance e a clareza do seu código.
Referências
-
TUTORIALSPOINT. C# - Delegates. Disponível em: https://www.tutorialspoint.com/csharp/csharp_delegates.htm. Acesso em: 01 mar. 2025.
-
IDC ONLINE. Understanding Delegates in C#. Disponível em: https://www.idc-online.com/technical_references/pdfs/information_technology/Understanding_Delegates_in_Csharp.pdf. Acesso em: 01 mar. 2025.
-
WAGNER, Bill. Understanding Delegates. Journal of Object Technology, [s.l.], jan. 2011. Disponível em: https://www.jot.fm/issues/issue_2004_05/column8.pdf. Acesso em: 01 mar. 2025.