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.
|
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.
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: