🎯 Objetivo: Neste capítulo, você irá dominar dois conceitos poderosos em C#: Delegates e Eventos. Juntos, eles permitem criar programas altamente desacoplados e baseados em eventos, onde componentes se comunicam de forma flexível, sem precisar saber a identidade uns dos outros.

Até agora, nossa comunicação entre classes foi direta: um objeto A chama um método de um objeto B. Mas e se o objeto A não souber qual método de B chamar? Ou se B nem existir ainda? Precisamos de uma forma mais flexível.


🤔 O Que São Delegates?

Imagine que você é o gerente de uma loja. Quando uma venda acontece, você precisa que várias pessoas sejam notificadas: o setor de relatórios, o contador e o estoque. Em vez de chamar cada um diretamente, você pode ter uma "lista de contatos" para a tarefa "notificar venda". Você só precisa se preocupar em chamar essa lista, e o sistema se encarrega de contatar a todos.

Um delegate em C# é exatamente isso: uma referência a um método. Ele é um tipo que define a assinatura de um método (tipo de retorno e parâmetros) e pode armazenar uma referência a um ou mais métodos compatíveis. Pense nele como um "ponteiro de função" seguro e gerenciado pelo .NET.

Comparação com Java: Em Java, você alcançaria uma funcionalidade similar usando Interfaces (como ActionListener) ou, em versões mais recentes, com Interfaces Funcionais e Lambdas. C# usa um tipo de primeira classe (delegate) para representar esse conceito.

✔️ Criando e Usando Delegates

  1. Declaração do Delegate: Primeiro, você declara o tipo do delegate, definindo a assinatura dos métodos que ele poderá referenciar.

    // Declara um delegate chamado NotificarVendaHandler.
    // Ele pode referenciar qualquer método que retorne void e receba (string, decimal).
    public delegate void NotificarVendaHandler(string produto, decimal valor);
    
  2. Referenciando um Método: Depois, você cria uma instância do delegate e atribui a ela um ou mais métodos que correspondam à sua assinatura.

    public class Relatorio
    {
        public void GerarRelatorioVenda(string produto, decimal valor)
        {
            Console.WriteLine($"Relatório: Venda de {produto} por R${valor} registrada.");
        }
    }
    
    public class Notificador
    {
        public void EnviarSMS(string produto, decimal valor)
        {
            Console.WriteLine($"Notificação: Venda de {produto} concluída. Enviando SMS.");
        }
    }
    
  3. Chamando o Delegate: Agora, você pode invocar o delegate, e ele irá chamar todos os métodos que foram atribuídos a ele.

    public class Main
    {
        public static void Main(string[] args)
        {
            var relatorio = new Relatorio();
            var notificador = new Notificador();
    
            // Cria uma instância do delegate e adiciona os métodos.
            // O operador += permite adicionar mais métodos.
            NotificarVendaHandler notificadorVenda = relatorio.GerarRelatorioVenda;
            notificadorVenda += notificador.EnviarSMS;
    
            Console.WriteLine("Iniciando notificação de venda...");
    
            // Invoca o delegate.
            notificadorVenda("Notebook", 3500.00m); 
            // Ambas as classes (Relatorio e Notificador) serão chamadas.
    
            // Você também pode remover um método com o operador -=
            notificadorVenda -= relatorio.GerarRelatorioVenda;
        }
    }
    

💥 Eventos: O Padrão de Design

Delegates são a base, mas eventos são o padrão de design que o C# oferece para usá-los com segurança. Um evento é uma variável do tipo delegate, mas com duas restrições cruciais:

  1. Só pode ser usado com os operadores += (para adicionar um assinante) e = (para remover um assinante).
  2. Só pode ser invocado (chamado) de dentro da classe que o declara.

Essa restrição protege o evento de ser acionado acidentalmente por código externo, garantindo que apenas a classe que "possui" o evento possa dispará-lo.

🧪 Exemplo Prático: Uma Loja com Eventos