CodeGym /Cursos /C# SELF /Criando eventos usando delegates (

Criando eventos usando delegates ( event e delegate)

C# SELF
Nível 52, Lição 1
Disponível

1. Introdução

Então, quando falamos de "evento" (event) no contexto do C#, nos referimos a um mecanismo que permite notificar de forma segura um ou mais objetos que algo aconteceu. Eventos não são mágica: por baixo dos panos são delegates (a palavra-chave delegate) com proteção extra contra uso incorreto.

Imagine assinar um canal do YouTube. O canal (o objeto-dono do evento) tem um botão "Inscrever-se" — qualquer espectador (outro objeto) pode clicar e entrar na lista de assinantes (delegates-handlers). Quando o autor publica um novo vídeo (dispara o evento), só os assinantes recebem a notificação, e só o dono do canal decide quando isso vai acontecer. Detalhe importante: espectadores podem se inscrever ou cancelar a inscrição, mas não podem enviar a notificação por conta própria — esse direito é só do dono do canal.

Um pouco de sintaxe formal


// Declaração do delegate, que vai definir a "forma" do handler do evento
public delegate void SimpleEventHandler();

// Declaração do evento baseado no delegate
public event SimpleEventHandler SomethingHappened;

É simples: event é a palavra-chave para declarar um evento, e o tipo do evento é sempre definido por um delegate.

2. Primeiro exemplo simples: evento "Botão foi clicado!"

Passo 1. Definimos o delegate para os handlers do evento


// Handler do evento: não retorna nada, não recebe parâmetros
public delegate void ButtonClickHandler();

Passo 2. Classe com o evento


public class Button
{
    // Evento: é possível assinar e cancelar, mas não é possível dispará-lo de fora
    public event ButtonClickHandler Click;

    // Método que "aperta o botão"
    public void Press()
    {
        Console.WriteLine("Botão pressionado!");
        // Chamada do evento: notificar todos os assinantes
        Click?.Invoke();
    }
}

Note: o evento é declarado com base no delegate, e no método Press o evento é chamado via ?.Invoke(). Por quê? Porque o evento pode estar vazio (ninguém assinou), então Click é null. O operador de chamada segura garante que os handlers sejam chamados só se houver assinantes.

Passo 3. Assinar o evento e rodar o código


// Exemplo de uso
public class Program
{
    public static void Main()
    {
        var button = new Button();

        // Adicionamos um "listener" (assinamos o evento)
        button.Click += OnButtonClicked;
        button.Click += () => Console.WriteLine("Mais um handler!");

        button.Press(); // Simula apertar o botão

        // Dá pra cancelar a inscrição do evento
        button.Click -= OnButtonClicked;
        button.Press();
    }

    // Handler normal do evento
    public static void OnButtonClicked()
    {
        Console.WriteLine("Handler: O botão foi pressionado!");
    }
}

Na primeira pressão ambos os handlers serão executados; na segunda — só a lambda.

3. Diferenças entre evento e delegate

A um delegate você pode atribuir livremente valores e até substituir completamente a lista de assinantes — alguém poderia escrever algo como button.Click = null e todas as assinaturas anteriores "sumiriam".

Com evento é diferente — ele é mais estrito em relação ao objeto. Só o dono da classe onde o evento é declarado pode dispará-lo diretamente (por exemplo, chamar Click() de dentro da classe). Qualquer outro código só pode assinar com += ou cancelar com -=, mas não pode "zerar" tudo de uma vez. Isso protege a encapsulação e evita "quebrar" o sistema de assinaturas.

4. Assinatura: tipos de delegates para eventos, parâmetros

A tradição no .NET é que um handler de evento recebe dois parâmetros — object sender (quem disparou o evento) e argumentos do evento (EventArgs). Recomenda-se usar os delegates EventHandler ou EventHandler<TEventArgs>.

Exemplo: delegate com parâmetros


public delegate void ButtonClickHandler(object sender, EventArgs e);

EventArgs é a classe base para passar informação adicional. Se precisar de mais dados, criamos uma subclasse.

Aplicando isso ao nosso botão


// Classe de argumentos do evento
public class ButtonClickEventArgs : EventArgs
{
    public string UserName { get; }

    public ButtonClickEventArgs(string userName)
    {
        UserName = userName;
    }
}

public class Button
{
    public event EventHandler<ButtonClickEventArgs> Click;

    public void Press(string userName)
    {
        Console.WriteLine("Botão pressionado!");
        Click?.Invoke(this, new ButtonClickEventArgs(userName));
    }
}

public static void Main()
{
    var button = new Button();
    button.Click += OnButtonClicked;

    button.Press("Vasiliy");
}

public static void OnButtonClicked(object sender, ButtonClickEventArgs e)
{
    Console.WriteLine($"O usuário {e.UserName} apertou o botão!");
}

5. Esquema visual: como o evento funciona


+-------------+
| Usuário     |
+-------------+
      |
      v
+--------------+
|  Button.Press|
+--------------+
      |
      v
+-----------------+         +------------------------+
|  Chamada Click? |----->---|   Assinante 1         |
+-----------------+         +------------------------+
      |                     |   Executar handler     |
      v                     +------------------------+
+-----------------+         +------------------------+
|   Se houver     |----->---|   Assinante 2         |
|   assinante     |         +------------------------+
      :                     |   Executar handler     |
      v                     +------------------------+

— Button.Press dispara o evento; os handlers dos assinantes são executados em sequência.

6. Dicas úteis

Forma recomendada de eventos no .NET

Use EventHandler e EventHandler<TEventArgs> para que o código seja compatível com bibliotecas e ferramentas. Essa abordagem facilita a evolução dos eventos: você adiciona novas propriedades aos seus EventArgs sem quebrar os assinantes.

Documentação: EventHandler, event.

Eventos vs delegates

Se você precisa ligar um componente a um método específico — um delegate é suficiente (delegate). Se diferentes partes do programa precisam poder assinar e cancelar a assinatura a qualquer momento — use um evento (event).

7. Erros comuns e situações incômodas ao criar eventos

Erro №1: chamar o evento sem checar null. Se não houver assinantes, tentar chamar o evento diretamente causará NullReferenceException. Use chamada segura:


Click?.Invoke(...);

Erro №2: tentar disparar o evento de fora da classe. Um evento só pode ser elevado (disparado) dentro da classe onde foi declarado. De outra classe o compilador dará erro — isso protege a encapsulação.

Erro №3: múltiplas inscrições do mesmo handler. Se um handler for assinado várias vezes, ele será chamado tantas vezes quanto foi assinado. Essa é uma característica do mecanismo de subscription. Fique de olho em duplicações de += e certifique-se de usar -= corretamente.

2
Tarefa
C# SELF, nível 52, lição 1
Bloqueado
Implementação de um evento simples "Botão foi pressionado"
Implementação de um evento simples "Botão foi pressionado"
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION