Eventos e EventHandler
ResumoUm evento é um delegate com regras de encapsulamento: a classe que o declara pode dispará-lo; quem está fora só pode inscrever-se (
+=) ou cancelar (-=). É o padrão publish/subscribe da BCL.
1. Objetivos de Aprendizagem#
- Declarar e disparar eventos.
- Inscrever e cancelar assinaturas.
- Usar a convenção
EventHandler/EventArgs.
2. Pré-requisitos#
- Delegates e lambdas (tópicos 9.1 e 9.2).
3. Conceito#
Um evento é construído sobre um delegate, mas com proteção: só a classe declarante pode invocá-lo. Assinantes usam += para registrar um handler e -= para remover.
A convenção do .NET usa EventHandler<TEventArgs>, cujo handler recebe (object? sender, TEventArgs e). O porquê: desacoplar quem gera o evento de quem reage — o publicador não conhece os assinantes.
4. Mão na Massa#
4.1. Setup#
dotnet new console -n Catalog.Eventos
cd Catalog.Eventos
4.2. Implementação Passo a Passo#
- Dados do evento e publicador:
public class ProdutoCriadoEventArgs : EventArgs
{
public string Nome { get; init; } = "";
}
public class Catalogo
{
public event EventHandler<ProdutoCriadoEventArgs>? ProdutoCriado;
public void Criar(string nome)
{
// ... salva o produto ...
ProdutoCriado?.Invoke(this, new ProdutoCriadoEventArgs { Nome = nome });
}
}
- Assinantes reagem:
var catalogo = new Catalogo();
catalogo.ProdutoCriado += (sender, e) => Console.WriteLine($"E-mail: '{e.Nome}' criado");
catalogo.ProdutoCriado += (sender, e) => Console.WriteLine($"Cache: invalidando '{e.Nome}'");
catalogo.Criar("Monitor");
// E-mail: 'Monitor' criado
// Cache: invalidando 'Monitor'
4.3. Executando#
dotnet run
5. Exemplo Completo#
O Catalogo dispara ProdutoCriado sem saber quem escuta; dois assinantes independentes reagem. Esse desacoplamento é a mesma ideia das notifications do MediatR vistas no curso de Web API.
6. Boas Práticas e Armadilhas#
| Faça | Evite |
|---|---|
Usar ?.Invoke para disparar (evita erro se não há assinantes) | Invocar o evento sem checar null |
Cancelar assinaturas (-=) quando não precisar mais | Deixar assinaturas presas (vazamento de memória) |
Seguir a convenção EventHandler | Expor o delegate cru como campo público |
Atençãoassinantes que nunca cancelam a assinatura mantêm o publicador "vivo" na memória (memory leak clássico). Em componentes de longa duração, cancele com
-=.
7. Segurança e Produção#
- Handlers que lançam exceção podem interromper o disparo dos demais. Em cenários críticos, isole cada handler (try/catch) ou use um mecanismo de mensageria mais robusto.
8. Exercícios#
- Fácil: adicione um terceiro assinante ao exemplo.
- Médio: crie um evento
PrecoAlteradocom o preço antigo e o novo emEventArgs. - Desafio: demonstre um vazamento por assinatura não cancelada e conserte-o.
9. Resumo#
Eventos são delegates encapsulados para publish/subscribe: a classe dispara, assinantes usam +=/-=. Siga a convenção EventHandler<T>, dispare com ?.Invoke e cancele assinaturas para evitar leaks.
10. Próximos Passos#
Encerramos delegates/eventos. A seguir, lidar com falhas: exceções.
11. Referências#
- Microsoft Learn — Eventos
- Microsoft Learn — Padrão de eventos padrão do .NET
- Microsoft Learn — EventHandler<TEventArgs>