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 PedidoEventArgs herdando de EventArgs com propriedades Id (int) e Valor (decimal).
  • Declare o evento PedidoCriado como EventHandler<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

  1. Criamos PedidoEventArgs herdando de EventArgs e adicionamos as propriedades Id e Valor, além de um construtor para inicializá-las.
  2. Na classe Pedido, declaramos o evento PedidoCriado do tipo EventHandler<PedidoEventArgs>?. Esse é o padrão do .NET para eventos.
  3. O método CriarPedido dispara o evento com PedidoCriado?.Invoke(this, new PedidoEventArgs(id, valor)). O primeiro argumento é o remetente (this).
  4. No Programa, definimos três métodos estáticos com a assinatura (object?, PedidoEventArgs). Eles serão os assinantes.
  5. No Main, criamos um Pedido e inscrevemos os três métodos com +=.
  6. Chamamos CriarPedido(1, 100.00m), disparando o evento para os três assinantes na ordem de inscrição.
  7. Cancelamos a inscrição do método EnviarEmail com -=.
  8. Chamamos CriarPedido(2, 200.00m). Agora apenas Log e AtualizarEstoque sã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 this no Invoke: 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ça NullReferenceException se 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