Desafio C#
Sistema de pedidos com eventos e cancelamento de inscrição
Enunciado
Implemente um sistema de pedidos onde a classe Pedido possui um evento PedidoCriado do tipo EventHandler<PedidoEventArgs>. Crie a classe PedidoEventArgs com uma propriedade Id e Valor. No Main, inscreva três assinantes: um para log, um para enviar e-mail e um para atualizar estoque. Após criar um pedido, cancele a inscrição do assinante de e-mail e crie outro pedido. Exiba as mensagens na ordem correta.
Requisitos
- Crie
PedidoEventArgsherdando deEventArgscom propriedadesId(int) eValor(decimal). - Declare o evento
PedidoCriadocomoEventHandler<PedidoEventArgs>?. - Implemente
CriarPedido(int id, decimal valor)que dispara o evento com os dados. - No
Main, inscreva três métodos nomeados (não lambdas) para permitir cancelamento. - Cancele a inscrição do assinante de e-mail antes do segundo pedido.
Código inicial
using System;
// TODO: crie PedidoEventArgs
public class Pedido
{
// TODO: declare o evento e o método CriarPedido
}
public class Programa
{
public static void Main()
{
// TODO: inscreva, crie pedidos e cancele inscrição
}
}
Saída esperada
Log: Pedido 1 criado, valor R$ 100,00
Email enviado para pedido 1
Estoque atualizado para pedido 1
Log: Pedido 2 criado, valor R$ 200,00
Estoque atualizado para pedido 2
Ver dica
Use EventHandler<PedidoEventArgs> e crie métodos estáticos no Programa para cada assinante. Para cancelar, use pedido.PedidoCriado -= EnviarEmail;.
Mostrar solução
using System;
// Argumentos do evento, herdando de EventArgs
public class PedidoEventArgs : EventArgs
{
public int Id { get; }
public decimal Valor { get; }
public PedidoEventArgs(int id, decimal valor)
{
Id = id;
Valor = valor;
}
}
public class Pedido
{
// Evento padrão do .NET
public event EventHandler<PedidoEventArgs>? PedidoCriado;
public void CriarPedido(int id, decimal valor)
{
// Dispara o evento com os argumentos
PedidoCriado?.Invoke(this, new PedidoEventArgs(id, valor));
}
}
public class Programa
{
// Assinantes nomeados para permitir cancelamento
static void Log(object? sender, PedidoEventArgs e)
=> Console.WriteLine($"Log: Pedido {e.Id} criado, valor R$ {e.Valor:F2}");
static void EnviarEmail(object? sender, PedidoEventArgs e)
=> Console.WriteLine($"Email enviado para pedido {e.Id}");
static void AtualizarEstoque(object? sender, PedidoEventArgs e)
=> Console.WriteLine($"Estoque atualizado para pedido {e.Id}");
public static void Main()
{
var pedido = new Pedido();
// Inscreve os três assinantes
pedido.PedidoCriado += Log;
pedido.PedidoCriado += EnviarEmail;
pedido.PedidoCriado += AtualizarEstoque;
// Cria o primeiro pedido (todos recebem)
pedido.CriarPedido(1, 100.00m);
// Cancela a inscrição do e-mail
pedido.PedidoCriado -= EnviarEmail;
// Cria o segundo pedido (sem e-mail)
pedido.CriarPedido(2, 200.00m);
}
}
Passo a passo
- Criamos
PedidoEventArgsherdando deEventArgse adicionamos as propriedadesIdeValor, além de um construtor para inicializá-las. - Na classe
Pedido, declaramos o eventoPedidoCriadodo tipoEventHandler<PedidoEventArgs>?. Esse é o padrão do .NET para eventos. - O método
CriarPedidodispara o evento comPedidoCriado?.Invoke(this, new PedidoEventArgs(id, valor)). O primeiro argumento é o remetente (this). - No
Programa, definimos três métodos estáticos com a assinatura(object?, PedidoEventArgs). Eles serão os assinantes. - No
Main, criamos umPedidoe inscrevemos os três métodos com+=. - Chamamos
CriarPedido(1, 100.00m), disparando o evento para os três assinantes na ordem de inscrição. - Cancelamos a inscrição do método
EnviarEmailcom-=. - Chamamos
CriarPedido(2, 200.00m). Agora apenasLogeAtualizarEstoquesão executados.
Por que funciona
O padrão EventHandler<T> é a maneira idiomática de declarar eventos no .NET. Ele encapsula um delegate multicast e segue a convenção (object sender, T args). A palavra-chave event restringe o acesso externo: só a classe Pedido pode disparar. O cancelamento com -= remove a referência do método da lista de invocação. Como usamos métodos nomeados, a remoção funciona corretamente; lambdas anônimas não podem ser removidas a menos que armazenadas em variável.
Erros comuns
- Usar lambda no
-=:pedido.PedidoCriado -= (s, e) => Console.WriteLine(...);não remove nada, pois cada lambda é uma instância diferente. Use métodos nomeados ou variáveis. - Esquecer de passar
thisnoInvoke:PedidoCriado?.Invoke(new PedidoEventArgs(...))não compila; a assinatura exige dois parâmetros. - Não herdar de
EventArgs: embora possível, o padrão recomenda herdar para consistência. - Disparar evento sem verificar nulo:
PedidoCriado(this, ...)lançaNullReferenceExceptionse ninguém se inscreveu. Use?.Invoke.
Outra forma de resolver
Usar Action<PedidoEventArgs> em vez de EventHandler<T>:
public event Action<PedidoEventArgs>? PedidoCriado;
// Disparo: PedidoCriado?.Invoke(new PedidoEventArgs(id, valor));
É mais simples, mas foge da convenção do .NET. Prefira EventHandler<T> para bibliotecas públicas.
Saída esperada
Log: Pedido 1 criado, valor R$ 100,00
Email enviado para pedido 1
Estoque atualizado para pedido 1
Log: Pedido 2 criado, valor R$ 200,00
Estoque atualizado para pedido 2