C# - Compreendendo delegates - I (revisitado)


 Neste artigo volto a abordar delegates apresentando o conceito e um exemplo prático usando a linguagem C# no Visual Studio 2015 Community.

Um delegate ou delegado é um ponteiro para funções, um ponteiro de tipo seguro, que pode conter referências a métodos de instância ou a métodos estáticos.



Os delegados são usados para chamar métodos dinamicamente em tempo de execução referenciando um método dentro de um objeto de delegação. (calma que vamos mostrar isso na prática...)

Na plataforma .NET, um delegado é declarado da seguinte maneira:
 


  
public delegate int TesteDelegate(int quantidade);
 

Neste código declaramos um delegado chamado "TesteDelegate" que pode chamar dinamicamente qualquer método que tenha um tipo de retorno "int" e aceite um único parâmetro também de tipo "int".

Essa é a grande sacada dos delegates, o 'tipo seguro', visto que eles somente podem chamar métodos que correspondem às suas assinaturas.

Internamente, a palavra-chave 'delegate', faz com que o compilador gere uma classe que deriva de System.MultiCastDelegate, que é a classe base para os delegates na plataforma .NET.

A classe MulticastDelegate, por sua vez, deriva da classe System.Delegate. A classe MulticastDelegate foi uma adição ao .NET Framework que primeiro incluía apenas a classe Delegate. A classe MulticastDelegate adicionou a capacidade de encadeamento de uma lista vinculada de delegados chamada "lista de invocação". Assim, um MulticastDelegate representa um delegado que pode chamar mais de um método.

Apresentando um exemplo trivial de delegates

Vou apresentar um exemplo trivial de uso de delegates apenas para mostrar a sintaxe e utilização básica deste recurso.

Crie um novo projeto no Visual Studio 2015 Community usando o template Windows Forms Application, com o nome Cshp_Delegates1, e inclua um botão de comando (btnDelegate) no formulário Form1.cs do projeto.

A seguir inclua o código abaixo:

using System;
using System.Windows.Forms;
namespace Cshp_Delegates1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        public delegate void MeuDelegate(string texto);
        private void btnDelegate_Click(object sender, EventArgs e)
        {
            MeuDelegate objDelegate = new MeuDelegate(MeuMetodo);
            objDelegate("Macoratti. net - Quase tudo para .NET.");
        }
        private void MeuMetodo(string texto)
        {
            MessageBox.Show(texto);
        }
    }
}

Vamos entender o código:

  public delegate void MeuDelegate(string texto);

Esta linha de código declara um delegate chamado 'MeuDelegate' o qual pode chamar qualquer método com tipo de retorno void e aceita um único parâmetro string.

  MeuDelegate objDelegate = new MeuDelegate(MeuMetodo)

No evento Click do botão de comando o código acima cria uma instância de 'MeuDelegate' chamada 'objDelegate' e adiciona o método 'MeuMetodo' para a lista de invocação. Isto significa que agora o delegate 'objDelegate' vai chamar o método 'MeuMetodo' dinamicamente em tempo de execução. Como ???

 objDelegate("Macoratti. net - Quase tudo para .NET.");

Esta linha de código faz com que o delegate 'objDelete' chame os métodos da lista de invocação. Neste exemplo, apenas o método 'MeuMetodo' será chamado. Clicando no botão você verá a mensagem 'Macoratti .net - Quase tudo para .NET' exibida pela execução do método.

Vamos alterar o código para mostrar como os delegates podem invocar múltiplos métodos. Inclua o código abaixo:

using System;
using System.Windows.Forms;
namespace Cshp_Delegates1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        public delegate void MeuDelegate(string texto);
        private void btnDelegate_Click(object sender, EventArgs e)
        {
            MeuDelegate objDelegate = new MeuDelegate(MeuMetodo);
                   objDelegate += new MeuDelegate(MeuMetodo2);
            objDelegate("Macoratti. net - Quase tudo para .NET.");
        }
        private void MeuMetodo(string texto)
        {
            MessageBox.Show(texto);
        }
        private void MeuMetodo2(string texto)
        {
            MessageBox.Show(texto);
        }
    }
}

Na linha de código  :  

    objDelegate += new MeuDelegate(MeuMetodo2);

incluímos um novo método - 'MeuMetodo2' - na lista de invocação do delegate. Assim, o delegate - objDelegate - agora vai chamar os métodos 'MeuMetodo' e 'MeuMetodo2'.

Observe que tivemos que definir o método 'MeuMetodo2' e que ele retorna void e recebe uma string como parâmetro, respeitando assim a assinatura do delegate.

Internamente os delegados possuem duas propriedades importantes:

  1. Target - Aponta para uma instância do objeto;
  2. Method - Aponta para um método dentro da instância do objeto;

Dessa forma, chamar um delegado significa chamar o método referenciado na propriedade Method que pertence à instância de objeto referenciada pela propriedade Target.

Para clarear um pouco o entendimento veja a figura abaixo:

Nesta figura indica que em nosso código nós adicionamos dois métodos : 'MeuMetodo' e 'MeuMetodo2' , na lista de invocação do delegate.

Em ambos os casos a propriedade Target é definida para 'this' , que é a instãncia da própria classe, enquanto que a propriedade Method é definida para o nome do método apropriado.

Observe como a ordem na qual os métodos foram incluídos na lista de invocação :

- Primeiro , 'MeuMetodo' é adicionado onde o ponteiro Previous é null
- A seguir , 'MeuMetodo2' é adicionado onde o ponteiro Previous aponta para 'MeuMetodo'

Agora creio que ficou mais claro como tudo funciona.

Mas apesar disso você pode estar se perguntando :

Qual o benefício em usar delegates nesse exemplo ?

Neste exemplo, realmente não temos nenhum benefício, ele foi usado apenas para mostrar como usar o recurso.

Na segunda parte do artigo vou abordar um cenário mais real onde usar delegates vai fazer muito mais sentido.

O anjo do Senhor acampa-se ao redor dos que o temem, e os livra.
Salmos 34:7

 

Referências:


José Carlos Macoratti