Construindo objetos Genéricos dinamicamente

Galerinha, tudo bem?

Neste post, quero compartilhar uma experiência que tive uns mêses atrás. A necessidade era de extender um ComboBox e implementar um método de pesquisa genérico, onde o ComboBox iria realizar uma pesquisa na tabela do tipo do objeto passado como parâmetro de configuração para o ComboBoxSearch, nome que demos ao novo componente. Quero apenas deixar claro que o intuito deste post, não é criar o componente ComboBox extendido, mas sim mostrar como podemos fazer uso de rotinas genéricas e usar este recurso de forma dinâmica, em runtime.

Vamos abrir o VS2010!

Criaremos uma aplicação windows forms, sete o título (Propriedade Text do formulário) para Generic in Runtime. Adicione dois botões e altere a propriedade Name para *btnInstanciar e btnInstanciarGenerico *e clique 2x para adicionarmos o evento em cada botão.Após ter criado e configurado o projeto, iremos criar as seguintes classes com suas propriedades, configurações e métodos:

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;

namespace GenericInRuntime  
{
    public class ValueObject
    {
        public Int32 Id { get; set; }
        public String Mensagem { get; set; }
    }
}

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;

namespace GenericInRuntime  
{
    public class Pessoa : ValueObject
    { }
}

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.Windows.Forms;

namespace GenericInRuntime  
{
    public class PesquisaGenerica<T> where T : ValueObject
    {
        public void ExibirMensagem(T objeto)
        {
            MessageBox.Show("Id: " + objeto.Id.ToString() + " - Mensagem: " + objeto.Mensagem);
        }

        public void ExibirMensagem2<T>()
        {
            Type tipo = typeof(T);

            MessageBox.Show("Chamada ao Método Genérico. Nome do Tipo Genérico: " + tipo.Name);
        }
    }
}

Com as classes criadas e configuradas, com suas propriedades e métodos. Vamos explicar agora o porque de cada uso genérico:

A criação da classe PesquisaGenerica recebe qualquer classe, porém colocamos uma condição nesta classe, onde as classes passadas como parâmetros genérico, tem que herdar de ValueObject. Essa é a condição que precisamos para passar qualquer tipo de classe para nossa classe de PesquisaGenerica. No método ExibirMensagem, estamos fazendo uso do tipo genérico T da nossa classe de pesquisa, para informar ao método que o objeto que será passado como parâmetro, será do mesmo tipo da classe de Pesquisa. Como nossa classe ValueObject possui essas duas propriedades: Id e Mensagem, então qualquer classe que herdar dela, também terá essas acesso a essas propriedades.

Agora com nossa estrutura de classes criada. Vamos começar a parte interessante da brincadeira. Criar as classes Dinamicamente, utilizando Reflection com Generics. 😀

Abaixo, teremos os códigos para os dois botões que temos no nosso formulário:

private void btnInstanciar_Click(object sender, EventArgs e)  
{
    object objeto = null;
    object objetoParametro = null;
    Type tipoGenerico = null;
    Type[] parametrosConstrutor = null;
    Type classeConstruida = null;

    try
    {
        tipoGenerico = typeof(PesquisaGenerica<>); parametrosConstrutor = new[] { typeof(Pessoa) };
        classeConstruida = tipoGenerico.MakeGenericType(parametrosConstrutor);
        MethodInfo mi = classeConstruida.GetMethod("ExibirMensagem", new Type[] { typeof(Pessoa) });
        objeto = Activator.CreateInstance(classeConstruida);
        objetoParametro = Activator.CreateInstance(typeof(Pessoa));
        ((Pessoa)objetoParametro).Id = 1;
        ((Pessoa)objetoParametro).Mensagem = "Execução de Método de Classe Genérica em Runtime";
        mi.Invoke(objeto, new object[] { objetoParametro });
    }
    catch (Exception ex)
    {
        MessageBox.Show("Erro: " + ex.Message);
    }
}

Neste evento temos, primeiramente, a recuperação do tipo Genérico. Sim, é exatamente desta forma que obtemos o tipo de uma classe Genérica. Como mostrado no código abaixo:

tipoGenerico = typeof(PesquisaGenerica<>);  

Logo em seguida, estamos configurando um array de objetos que determinará qual será o parâmetro genérico que iremos passar para a nossa classe PesquisaGenerica:

parametrosConstrutor = new[] { typeof(Pessoa) };  

Para instanciarmos uma classe genérica, temos que fazer uso do método MakeGenericType, como no código abaixo:

classeConstruida = tipoGenerico.MakeGenericType(parametrosConstrutor);  

Para obtermos o método que queremos da classe em runtime, temos que fazer uso do método GetMethod, mas como sabemos que o método recebe um parâmetro que é um objeto do tipo T, ou seja, é do mesmo tipo que a classe PesquisaGenerica será configurada. Então, estamos informando que o métod que estamos querendo executar, tem um parâmetro que é do tipo Pessoa:

MethodInfo mi = classeConstruida.GetMethod("ExibirMensagem", new Type[] { typeof(Pessoa) });  

Agora estamos criando, instanciando os objetos que farão parte do uso do método em runtime:

objeto = Activator.CreateInstance(classeConstruida); objetoParametro = Activator.CreateInstance(typeof(Pessoa));  

E por fim, executamos o método, passando como parâmetro uma instância de um objeto do tipo Pessoa:

mi.Invoke(objeto, new object[] { objetoParametro });  

Agora iremos mostrar como chamaremos o método genérico da nossa classe PesquisaGenerica, apenas como ressalva, o que mudará será apenas uma chamada a mais ao método MakeGenericMethod e não iremos passar nenhum parâmetro ao Método, apenas iremos tipar o Método, como mostrado no código abaixo:

private void btnInstanciarGenerico_Click(object sender, EventArgs e)  
{
    object objeto = null;
    Type tipoGenerico = null; Type[] parametrosConstrutor = null;
    Type classeConstruida = null;

    try
    {
        tipoGenerico = typeof(PesquisaGenerica<>);
        parametrosConstrutor = new[] { typeof(Pessoa) };
        classeConstruida = tipoGenerico.MakeGenericType(parametrosConstrutor);
        MethodInfo mi = classeConstruida.GetMethod("ExibirMensagem2", new Type[] { });

        MethodInfo miConstructed = mi.MakeGenericMethod(parametrosConstrutor);
        objeto = Activator.CreateInstance(classeConstruida);
        miConstructed.Invoke(objeto, null);
    }
    catch (Exception ex)
    {
        MessageBox.Show("Erro: " + ex.Message);
    }
}

Aqui está o novo código com algumas mudanças referente ao primeiro evento.

Bom pessoal, chamadas á métodos de maneira dinâmica é um excercício bastante interessante para sabermos como funciona, por trás dos bastidores, a estrutura de classes e métodos Genéricos.

Até o próximo Post!

Comments