C# - Delegates e Eventos : Conceitos básicos


  Neste artigo vou abordar um assunto importante da linguagem C# : Delegates e Events.

Todos os exemplos mostrados neste artigo foram feito usando o Visual Studio Community.

Um delegate é um elemento da linguagem C# que permite que você faça referência a um método.

Nota:  Um delegate é similiar ao conceito de ponteiros para funções usados nas linguagens C e C++

Então um delegate em C#, é semelhante a um ponteiro de função (a vantagem é que é um ponteiro seguro). Usando um delegate você pode encapsular a referência a um método dentro de um objeto de delegação.

O objeto de delegação pode ser passado para o código o qual pode chamar o método referenciado sem ter que saber em tempo de compilação qual método será chamado.

Ora bolas...porque eu preciso de uma referência para um método ???

Quer uma resposta curta e grossa ???

R: Usando delegates você tem a flexibilidade para implementar qualquer funcionalidade em tempo de execução..

Como isso funciona ???

Vejamos um exemplo de chamada de método diretamente sem usar delegates.

Abaixo vemos o código onde temos a chamada do método Processar sendo feita diretamente após criamos uma instância da classe Macoratti.

using System;

namespace Macoratti.SemUsarDelegates
{
    class Program
    {
        static void Main(string[] args)
        {
            Macoratti macClass = new Macoratti();
            macClass.Processar();
            Console.ReadKey();
        }

        public class Macoratti
        {
            public void Processar()
            {
                Console.WriteLine("Processar() Inicio");
                Console.WriteLine("Processar() Fim");
            }
        }
    }
}
 

Não há segredos aqui.
Tudo funciona como esperado.

 

E se não desejamos chamar o método diretamente ? E se quiséssemos que outro processo fizesse a chamada ao método ??

Vamos imaginar que você quisesse criar um algoritmo que fosse muito flexível e reutilizável permitindo implementar uma funcionalidade diferente na medida do necessário ?

Este cenário pode ser útil em um sistema orientado a eventos como uma interface gráfica onde um código é executado quando um usuário clica em um botão.

Um recurso interessante de um delegate é que ele não sabe nem quer saber nada sobre a classe do objeto que ele esta referenciando. Qualquer objeto pode fazer isso; o que importa é que os tipos dos argumentos do método e o tipo de retorno coincida com o do delegate. Essa propriedade faz com que os delegates sejam adequados para a invocação anônima.

Obs: Um delegate é uma classe e quando criamos uma instância do delegate passamos o nome da função (como um parâmetro para o construtor do delegate) para o qual o delegate irá referenciar.

Todo o delegate possui uma assinatura. Por Exemplo :

Delegate int ExemploDeDelegate(string nome, bool b);

No exemplo acima temos um Delegate com uma assinatura de onde temos que:

A assinatura de um delegate é composta de:

Agora considere o método a seguir:

private int ExemploDeFunction(string str, bool bln)
{...}

Como o método acima possui uma assinatura igual a do delegate ela pode ser passado para o delegate da seguinte forma:


ExemploDeDelegate macoratti = new ExemploDeDelegate(ExemploDeFunction);

Na declaração acima temos que:

- Agora macoratti refere-se ao método ExemplodeFunction ou seja ExemplodeFunction esta registrada para macoratti, e se você chamar macoratti, o método ExemplodeFunction será invocado.

Existem três etapas na definição e uso de delegates :

Vejamos então um exemplo bem simples de como criar e usar um delegate que ilustra as etapas acima:

using System;

namespace Macoratti.SimplesDelegate
{
    // Declaração
    public delegate void SimplesDelegate();

    class ExemploDeDelegate
    {
        public static void minhaFuncao()
        {
            Console.WriteLine("Eu fui chamada por um delegate ...");
        }

        public static void Main()
        {
            // Instanciação
            SimplesDelegate simplesDelegate = new SimplesDelegate(minhaFuncao);

            // Invocação
            simplesDelegate();
            Console.ReadKey();
        }
    }
}
 
 

Note que ao invocar o delegate temos a execução do método minhaFuncao().

Eventos

Eventos em C# são uma maneira de uma classe fornecer notificações aos usuários da classe quando alguma coisa ocorre com o objeto da classe.

Eventos, utilizam os delegates para fazer a comunicação entre as mensagens da aplicação e os nosso métodos.

O uso mais comum para os eventos são as interfaces de usuário onde normalmente as classes que representam os controles na interface possuem eventos que são notificados quando o usuário realiza uma operação no controle, como clicar um botão.

Um botão é uma classe, quando você clicar nele, o evento Click é acionado;
Um timer é uma classe, cada um milésimo de segundo evento Tick é acionado;

Os eventos em C# são introduzidos junto com os delegates.

Por convenção, os manipuladores de eventos do .NET Framework retornam void e recebem dois parâmetros.

  1. O primeiro parâmetro é a 'fonte' do evento (ou seja, o objeto que faz a publicação). ;
  2. O segundo parâmetro é um objeto derivado do EventArgs. (É recomendável que seus manipuladores de eventos sigam esse padrão.);
As aplicações desenvolvidas em .NET são “message-based” o que significa que a aplicação se comunica com o windows e com ela própria através de
mensagens.

O Receiver ou Handler, é quem está esperando a mensagem do evento. É aqui que os delegates entram em acção.

O Sender, será responsável por gerar (Raise) o evento; No caso de eventos do mouse ou teclado o sender é o .NET runtime.

Nota: o sender não tem conhecimento para quem está enviando a mensagem, logo eles utilizam os delegates;

A assinatura de uma função que recebe o evento sempre esta sem retorno e possui como parâmetros um objeto e uma classe derivada de EventArgs

Para declarar um evento em C# use a seguinte sintaxe:


public delegate void testeDelegate(int a);
public event testeDelegate MeuEvento;

Uma vez que um evento é declarado, ele deve ser associado com um ou mais manipuladores de eventos antes que possa ser invocado. Um manipulador de eventos nada mais é que um método que é chamado através de um delegate.

Use o operador + = para associar um evento com uma instância de um delegate que já existe. Exemplo:


Meuform.MeuEvento += new testeEvent(MeuMetodo);

Vejamos a seguir um exemplo bem simples e intuitivo mostrando o uso de delegates e eventos:

Objetivo : Definir um controle Button que exiba uma mensagem quando for clicado

Crie uma aplicação Windows Forms e inclua um controle Button no formulário form1.cs e defina um evento Click para este Button:

O código referente ao Button criado no juntamente como evento Click pode ser visto abaixo:

            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(90, 28);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(102, 41);
            this.button1.TabIndex = 0;
            this.button1.Text = "button1";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);

Defina agora o código abaixo onde temos a definição a mensagem no evento Click e da rotina Mensagem:

  using System.Windows.Forms;

namespace C_Delegates_Events
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Este é o evento Click do Button1");
        }

        public void Mensagem(object sender, System.EventArgs e)
        {
            MessageBox.Show("Macoratti.net  - método Mensagem");
        }
    }
}

Ao executar o projeto e clicar no botão iremos obter:

Vamos agora alterar a definição do evento Click conforme a seguir:

this.button1.Click += new System.EventHandler(Mensagem);

Se executarmos novamente o projeto e clicarmos no botão de comando iremos obter:

Agora estamos invocando o método Mensagem() quando o usuário clicar no botão e exibindo a respectiva mensagem.

Para encerrar este artigo vou mostrar um exemplo mais prático de utilização de delegates.

Calculadora com delegates

Abra o Visual C# 2010 Express Edition e crie um novo projeto do tipo Windows Application chamado CalculadoraDelegates;

No formulário form1.cs inclua os controles:

Conforme o leiaute da figura abaixo:

Vamos incluir uma classe no projeto na qual irei definir o delegate;

No menu Project selecione Add Class, informe o nome Calculadora e clique em Add;

A seguir defina o seguinte código nesta classe:

namespace CalculadoraDelegates
{

    //definindo o delegate
    public delegate int Calcular(int value1, int value2);

    //A classe que contém os métodos
    //que serão atribuidos aos objetos delegates

    public class Calculadora
    {
        //o método Somar que será atribuido ao objeto delegate
        //e possui a assinatura EXATA do delegate

        public int Somar(int value1, int value2)
        {
            return value1 + value2;
        }

        //o método Subtrair que será atribuido ao objeto delegate
        //e possui a assinatura EXATA do delegate

        public int Subtrair(int value1, int value2)
        {
            return value1 - value2;
        }

        //o método Multiplicar que será atribuido ao objeto delegate
        //e possui a assinatura EXATA do delegate

        public int Multiplicar(int value1, int value2)
        {
            return value1 * value2;
        }

        //o método Dividir que será atribuido ao objeto delegate
        //e possui a assinatura EXATA do delegate

        public int Dividir(int value1, int value2)
        {
            return value1 / value2;
        }
    }
}

Nesta classe definimos o delegate Calcular e os métodos Somar, Subtrair, Multiplicar e Dividir que possuem a assinatura idêntica ao delegate.

No formulário form1.cs vamos definir o código que vai usar o delegate e chamar as funções conforme abaixo:

Obs: Para facilitar a compreensão eu estou exibindo somente o código relacionado a utilização do delegate.

using System.Windows.Forms;
using System;

namespace CalculadoraDelegates
{

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        int valor1=0;
        int valor2=0;

        //criando a instância da classe a qual contém os métodos
        //que serão atribuidos aos objetos delegates

        static Calculadora calc = new Calculadora();

        //criando objetos delegates e atribuindo os métodos apropriados
        //que possuem a assinatura EXATA do delegate

        Calcular Somar       = new Calcular(calc.Somar);
        Calcular Subtrair    = new Calcular(calc.Subtrair);
        Calcular Multiplicar = new Calcular(calc.Multiplicar);
        Calcular Dividir       = new Calcular(calc.Dividir);

      private void btnSomar_Click(object sender, System.EventArgs e)
        {
            valor1 = Convert.ToInt32(txtValor1.Text);
            valor2 = Convert.ToInt32(txtValor2.Text);
            txtResultado.Text = Somar(valor1, valor2).ToString();
        }

        private void btnSubtrair_Click(object sender, EventArgs e)
        {
            valor1 = Convert.ToInt32(txtValor1.Text);
            valor2 = Convert.ToInt32(txtValor2.Text);
            txtResultado.Text = Subtrair(valor1, valor2).ToString();
        }

        private void btnMultiplicar_Click(object sender, EventArgs e)
        {

            valor1 = Convert.ToInt32(txtValor1.Text);
            valor2 = Convert.ToInt32(txtValor2.Text);
            txtResultado.Text = Multiplicar(valor1, valor2).ToString();
        }

        private void btnDividir_Click(object sender, EventArgs e)
        {
            valor1 = Convert.ToInt32(txtValor1.Text);
            valor2 = Convert.ToInt32(txtValor2.Text);
            txtResultado.Text = Dividir(valor1, valor2).ToString();
        }
   }
}

Observe como usamos o delegate para invocar os métodos definidos na classe Calculadora.

Isto foi possível pois todos os métodos tinham a mesma assinatura do delegate definido.

Creio que com este exemplo ficou mais claro como definir e usar delegates.

Pegue o projeto completo aqui: CalculadoraDelegates.zip

"Não se turbe o vosso coração;crede em Deus, crede também em mim." (João 14:1)

Referências:


José Carlos Macoratti