Delegates e Eventos
Parte da seção Programação Orientada a Objetos do Journey de C# da Coddy — lição 43 de 70.
Um delegate é um tipo que representa uma referência a um método. Pense nele como uma variável que contém um método em vez de dados. Isso permite que você passe métodos como argumentos, armazene-os em coleções e os chame dinamicamente.
Você declara um delegate especificando a assinatura do método que ele pode referenciar:
public delegate void Notify(string message);
public class Publisher
{
public void SendAlert(string msg) => Console.WriteLine("Alert: " + msg);
}
// Uso
Notify notifier = new Publisher().SendAlert;
notifier("System started"); // Alert: System startedEventos baseiam-se em delegates para implementar o padrão publish-subscribe. Uma classe dispara um evento, e outras classes podem se inscrever para receber notificações. A palavra-chave event restringe como o delegate pode ser usado - assinantes podem apenas adicionar ou remover manipuladores, não invocar o evento diretamente.
public class Button
{
public event Action Clicked;
public void Click()
{
Clicked?.Invoke(); // Notifica todos os assinantes
}
}
// Inscrevendo-se
Button btn = new Button();
btn.Clicked += () => Console.WriteLine("Button was clicked!");
btn.Click(); // O botão foi clicado!O operador += inscreve um manipulador, enquanto o -= cancela a inscrição. O padrão ?.Invoke() chama o evento com segurança apenas se houver assinantes. Eventos são fundamentais para aplicações GUI, motores de jogos e qualquer sistema onde objetos precisam reagir a mudanças sem acoplamento rígido.
Desafio
FácilVamos construir um sistema de monitoramento de temperatura que utiliza delegates e eventos para notificar os assinantes sempre que a temperatura mudar. Este é um exemplo clássico do padrão publish-subscribe que os eventos permitem de forma tão elegante.
Você organizará seu código em dois arquivos:
TemperatureMonitor.cs: Crie uma classeTemperatureMonitorno namespaceMonitoringque atua como o publicador. Seu monitor deve:- Ter um campo privado para armazenar a temperatura atual
- Declarar um evento usando
Action<int>chamadoTemperatureChangedque é disparado sempre que a temperatura é atualizada - Ter uma propriedade
Temperature(int) onde o setter atualiza o campo privado e dispara o evento com o novo valor da temperatura
?.Invoke()para disparar o evento com segurança apenas quando houver assinantes.Program.cs: No seu arquivo principal, crie umTemperatureMonitore assine dois manipuladores diferentes para o seu eventoTemperatureChanged:- Um manipulador que imprime:
Alert: Temperature is now {value} degrees - Outro manipulador que imprime:
Logged: {value}
- Um manipulador que imprime:
Você receberá duas entradas:
- Primeiro valor de temperatura (inteiro)
- Segundo valor de temperatura (inteiro)
Por exemplo, se as entradas forem 25 e 30, a saída deve ser:
Alert: Temperature is now 25 degrees
Logged: 25
Alert: Temperature is now 30 degrees
Logged: 30Observe como a definição da propriedade Temperature notifica automaticamente todos os assinantes - o monitor não precisa saber nada sobre quem está ouvindo. Essa é a beleza da abordagem orientada a eventos!
Folha de consulta
Um delegate é um tipo que representa uma referência a um método, permitindo que você passe métodos como argumentos, armazene-os em coleções e os chame dinamicamente.
Declare um delegate especificando a assinatura do método:
public delegate void Notify(string message);
public class Publisher
{
public void SendAlert(string msg) => Console.WriteLine("Alert: " + msg);
}
// Usage
Notify notifier = new Publisher().SendAlert;
notifier("System started"); // Alert: System startedEventos implementam o padrão publish-subscribe (publicador-assinante) usando delegates. A palavra-chave event restringe o uso do delegate - os assinantes podem apenas adicionar ou remover manipuladores, não invocar o evento diretamente.
public class Button
{
public event Action Clicked;
public void Click()
{
Clicked?.Invoke(); // Notify all subscribers
}
}
// Subscribing
Button btn = new Button();
btn.Clicked += () => Console.WriteLine("Button was clicked!");
btn.Click(); // Button was clicked!Use += para assinar um manipulador e -= para cancelar a assinatura. O padrão ?.Invoke() chama o evento com segurança apenas se houver assinantes.
Experimente você mesmo
using System;
using Monitoring;
class Program
{
public static void Main(string[] args)
{
// Ler entrada
int temp1 = Convert.ToInt32(Console.ReadLine());
int temp2 = Convert.ToInt32(Console.ReadLine());
// TODO: Criar uma instância de TemperatureMonitor
// TODO: Inscrever um manipulador que imprime: "Alert: Temperature is now {value} degrees"
// TODO: Inscrever outro manipulador que imprime: "Logged: {value}"
// TODO: Definir a temperatura para temp1
// TODO: Definir a temperatura para temp2
}
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos ExternosNamespaces e DiretivasIntrodução a Classes e ObjetosA Palavra-chave 'this'Métodos e ParâmetrosCampos vs PropriedadesConstrutoresInicializadores de ObjetosRecapitulação - Calculadora Simples4Herança
Sintaxe Básica de Herança (:)A Palavra-chave 'base'Palavras-chave Virtual e OverrideClasses SeladasA Classe Base 'object'Recapitulação - Hierarquia de Funcionários7Recursos Avançados
Sobrecarga de OperadoresIndexadores (this[])Sobrescrita de ToString()Métodos de ExtensãoRecapitulação - Lista Personalizada10Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoSingleton Thread-SafePadrão FactoryPadrão Observer (Eventos)Padrão Strategy2Propriedades e Membros Estáticos
Propriedades AutoimplementadasPropriedades de Apenas Leitura/EscritaCampos e Métodos EstáticosClasses EstáticasMembros com Corpo de Expressão5Polimorfismo e Interfaces
Polimorfismo: Compilação vs ExecuçãoInterface vs Classe AbstrataMúltiplas InterfacesInterfaces ExplícitasUpcasting e DowncastingRecapitulação - Calculadora de Formas8Conceitos Avançados de POO
Composição sobre HerançaGenerics (Classes e Métodos)Delegates e EventosAtributos e ReflectionIDisposable e a Instrução usingFundamentos de Injeção de Dependência11Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão Composite3Arquitetura de Classes
Dados de Instância vs EstáticosPalavras-chave 'readonly' e 'const'Campos de SuporteRecapitulação - Gerenciador de Conta Bancária6Encapsulamento
Modificadores de AcessoPropriedades para EncapsulamentoImplementação de Ocultação de DadosPadrões de ImutabilidadeRecapitulação - Registros de Estudantes9Argumentos Variáveis
A Palavra-chave 'params'Parâmetros OpcionaisArgumentos NomeadosSobrecarga de Métodos