C# - Eventos (revisitado)


A abordagem da linguagem C# além de orientada a objetos também é orientada a eventos. Isto significa que enquanto atividades ocorrem, uma classe pode gerar eventos para indicar a ação que aconteceu, e passar informações relacionadas a outras classes que tenham aderido ao evento. Estes eventos podem ser utilizados para vários fins, incluindo a indicação do progresso de um processo de longa duração.

Uma das principais vantagens do uso de eventos é que uma classe que publica um evento não precisa saber os detalhes dos assinantes em tempo de projeto. Como os eventos são baseadas em delegados (delegates), os assinantes só são adicionados quando o programa está sendo executado.

Existem três etapas para criar um manipulador de eventos dentro de uma classe na linguagem C#:

  1. Em primeiro lugar é preciso de um delegado.O delegado é usado para armazenar os dados dos assinantes para o evento;
  2. Em segundo lugar, um evento público deve ser criado. Este evento é visível externamente à classe e é usado para criar as inscrições em eventos;
  3. Finalmente, um método deve ser criado dentro da classe. O método contém o código que aciona o evento;

Para fornecer um exemplo da utilização de eventos, vamos criar um programa contendo uma classe que representa um carro. Quando a velocidade do carro ultrapassar um limite de segurança, um evento deverá ser disparado.

Abra o Visual C# 2010 Express Edition e no menu File->New Project selecione o template Console Application e informe o nome "CSharp_Eventos";

Adicione um arquivo de classe ao projeto no menu Project-> Add Class com o nome "Carro" e adicione o seguinte código à classe para criar uma propriedade somente leitura  Velocidade e um método Acelerar().

A variável _velocidadeSeguranca define a velocidade máxima permitida e será utilizada mais tarde.

class Carro
{
   private int _velocidade = 0;
   private int _velocidadeSeguranca = 70;

   public int Velocidade
   {
      get
      {
           return _velocidade;
      }
    }

    public void Acelerar(int kmh)
    {
       _velocidade += kmh;
   }
}

Criando o Delegate

O delegado para um evento é declarado usando a sintaxe padrão para qualquer delegado. No entanto, deve ser declarado com um tipo de dados vazio . Isto significa que vários assinantes podem ser ligados a um único evento com cada um recebendo notificação do evento.

A assinatura do delegado é importante, pois esta é a assinatura que os assinantes vão ver. Para cumprir com o padrão esperados, a assinatura deve conter dois parâmetros:

1- O primeiro parâmetro deve ser um objeto chamado 'source'. Este irá conter uma referência ao objeto que disparou um evento;
2- O segundo parâmetro deve ser do tipo
EventArgs e deve ser nomeado 'e'. EventArgs é uma classe especial que pode ser derivada para criar classes personalizadas para passar informações quando um evento é acionado;

Neste exemplo, vamos adicionar um evento para a classe Carro que é disparado quando o carro ultrapassa a velocidade limite de segurança. Para criar o delegado para este evento, adicione o seguinte código para o namespace (fora da definição de classe).

delegate void VelocidadeSegurancaExceededEventHandler(object source, EventArgs e);

Declarando o Evento

O evento é o elemento publicamente visível da classe. Ele pode ser assinado por outras classes que pretendem reagir ao evento que está sendo emitido. O evento é declarado prefixando o nome com a palavra-chave do evento e o nome do delegado no qual o evento será baseado.

Para criar o evento para a classe Carro , adicione o seguinte código na classe Carro:

public event VelocidadeSegurancaExcedidaEventHandler ExcedeuVelocidadeSeguranca;

Criando um método de evento

Um método de evento é um método único usado para disparar um evento. Embora não seja estritamente necessário para criar um método, é vantajoso, pois torna a manutenção do código mais simples e permite que classes derivadas substituam a funcionalidade e alterem a maneira pela qual os eventos são disparados.

Para criar o método para o evento VelocidadeSegurancaExcedidaEventHandler adicione o seguinte código para a classe de carro.

 public virtual void OnVelocidadeSegurancaExcedida(EventArgs e)
 {
            if (ExcedeuVelocidadeSeguranca != null)
                ExcedeuVelocidadeSeguranca(this, e);
}

Há vários itens a serem observados neste método. Em primeiro lugar, o método é marcado como virtual. Isso significa que a funcionalidade do método pode ser substituída e alterado por classes que são derivadas a partir da classe Carro.

O método é chamado usando o nome do evento e o prefixo 'On'. Esta é uma simples convenção que os desenvolvedores seguem. O método de evento requer apenas um único parâmetro que contenha os argumentos do evento para o evento gerado.

Este parâmetro trata toda a informação que deve ser passada para os assinantes do evento.

Dentro do bloco de código do método, uma instrução if verifica se o evento é definido como nulo. Esta afirmação condicional verifica se há pelo menos um assinante para o evento bem como se não existe nenhum assinante, pois neste caso disparar o evento causaria uma exceção.

Se existem assinantes o evento é gerado como se fosse um método, passando uma referência ao objeto atual e os argumentos do evento que foram passados no parâmetro.

Chamando o método de evento

Para completar o exemplo, o método do evento precisa ser chamada quando o carro acelera passado a velocidade de segurança. Altere método Acelerar da seguinte maneira para que o evento seja gerado quando a mudança da velocidade do carro superar a velocidade limite de segurança:

public void Acelerar(int kmh)
{
  int velocidadeAnterior = _velocidade;
  _velocidade += kmh;

  if (velocidadeAnterior <= _velocidadeSeguranca
&& _velocidade > _velocidadeSeguranca)
       OnVelocidadeSegurancaExcedida(new EventArgs());
}

Assinando um evento

Para assinar um evento, uma referência ao método deve ser adicionado ao evento. Isto é conseguido de uma maneira semelhante à adição de referências a um delegado multicast. Cada método que é para ser chamado quando o evento é disparado é adicionado ao evento usando o operador de atribuição e adição composto(+=). A assinatura dos métodos adicionados devem coincidir com o delegado no qual o evento está baseado.

Vamos adicionar um novo método que captura o evento que ocorre quando um carro ultrapassa a velocidade de segurança. Este método, chamado CarroLimiteVelocidadeExcedida, é adicionado à classe Program da aplicação de console.

Ele irá obter a velocidade do objeto origem e emitir uma mensagem de aviso. Os dois parâmetros do método coincidem com o evento delegado e receberão o objeto carro disparando o evento e os argumentos de eventos associados.

 static void CarroLimiteVelocidadeExcedida(object source, EventArgs e)
 {
     Carro carro = (Carro)source;
     Console.WriteLine("O limite de velocidade foi excedido ({0}Kmh)", carro.Velocidade);
 }

Assinando o evento ExcedeuVelocidadeSeguranca

Agora que já temos o evento e o método que reage ao evento criados vamos criar um objeto carro e assinar o evento. Para isso inclua o código abaixo no método Main da classe Program:

Carro carro = new Carro();
carro.ExcedeuVelocidadeSeguranca += new VelocidadeSegurancaExcedidaEventHandler(CarroLimiteVelocidadeExcedida);

Para testar o evento modifique o método Main() da classe Program conforme abaixo:

static void Main(string[] args)
{

            Carro carro = new Carro();
            carro.ExcedeuVelocidadeSeguranca += new VelocidadeSegurancaExcedidaEventHandler(CarroLimiteVelocidadeExcedida);

            for (int i = 0; i < 3; i++)
            {
                carro.Acelerar(30);
                Console.WriteLine(" Velocidade atual : {0} Kmh ", carro.Velocidade);
            }
            Console.ReadKey();
}

Executando o projeto o laço for irá executar o método Acelerar 3 vezes iniciando com a velocidade 30 depois 60 e finalmente 90 Kmh. Neste último caso teremos que o evento será disparado pois o limite definido para velocidade é igual a 70.

O resultado pode ser visto abaixo:

O código completo do projeto é dado a seguir:

using System;

namespace CSharp_Eventos
{
    class Program
    {
        static void Main(string[] args)
        {
            Carro carro = new Carro();
            carro.ExcedeuVelocidadeSeguranca += new VelocidadeSegurancaExcedidaEventHandler(CarroLimiteVelocidadeExcedida);

            for (int i = 0; i < 3; i++)
            {
                carro.Acelerar(30);
                Console.WriteLine(" Velocidade atual : {0} Kmh ", carro.Velocidade);
            }
            Console.ReadKey();
        }

        static void CarroLimiteVelocidadeExcedida(object source, EventArgs e)
        {
            Carro carro = (Carro)source;
            Console.WriteLine("O limite de velocidade foi excedido ({0}Kmh)", carro.Velocidade);
        }
    }

    delegate void VelocidadeSegurancaExcedidaEventHandler(object source, EventArgs e);
    
    class Carro
    {
        public event VelocidadeSegurancaExcedidaEventHandler ExcedeuVelocidadeSeguranca;

        private int _velocidade = 0;
        private int _velocidadeSeguranca = 70;

        public int Velocidade
        {
            get
            {
                return _velocidade;
            }
        }

        public void Acelerar(int kmh)
        {
            int velocidadeAnterior = _velocidade;
            _velocidade += kmh;

            if (velocidadeAnterior <= _velocidadeSeguranca && _velocidade > _velocidadeSeguranca)
                OnVelocidadeSegurancaExcedida(new EventArgs());
        }

        public virtual void OnVelocidadeSegurancaExcedida(EventArgs e)
        {
            if (ExcedeuVelocidadeSeguranca != null)
                ExcedeuVelocidadeSeguranca(this, e);
        }
    }
}

Pegue o projeto completo aqui: CSharp_Eventos.zip

Rom 8:29 Porque os que dantes conheceu, também os predestinou para serem conformes à imagem de seu Filho, a fim de que ele seja o primogênito entre muitos irmãos;
Rom 8:30
e aos que predestinou, a estes também chamou; e aos que chamou, a estes também justificou; e aos que justificou, a estes também glorificou.

Referências:


José Carlos Macoratti