C# - Usando Reflection (revisitado) - II


 Neste artigo vamos recordar o conceito de Reflection e sua utilização na linguagem C# e o conceito de Late Binding.

Quando desenvolvemos usando a linguagem  C#  especificamos os tipos de todas as suas variáveis, valores de retorno e parâmetros do método no código. O compilador verifica todos os tipos quando você constrói seu software e garante que você não tente inadvertidamente usar membros que não existam ou atribuíram um valor incorreto a um tipo. Isso é conhecido como Early Binding.

Na primeira parte do artigo recordamos o conceito e usamos como exemplo uma classe Cliente onde definimos métodos, construtores e propriedades e usando Reflection obtemos informações da classe usando o Early Binding.

Neste artigo vamos assumir que não temos conhecimento da classe Cliente em tempo de compilação.Neste cenário vamos obter as informações em tempo de execução usando o Late Binding.

Os tipos que estão vinculados tardiamente são verificados apenas quando são usados em tempo de execução. O Late Binding ou ligação tardia é ideal para criar software configurável, como aquele que possui um modelo de plug-in. Em tal software você pode permitir que o usuário especifique os nomes dos tipos e métodos a serem usados em um arquivo de configuração. O software usaria esses dados de configuração para carregar assemblies em tempo de execução, sem conhecimento prévio dos plug-ins ao escrever o aplicativo principal.

Recursos Usados:

Criando o projeto Console

Abra a solução CShp_Reflection criada no VS 2017 Community no artigo anterior.

Altere o código da classe Cliente incluindo o método GetNomeCompleto:

using static System.Console;
namespace CShp_Reflection
{
    class Cliente
    {
        public int Id { get; set; }
        public string Nome { get; set; }
        public Cliente(int id, string nome)
        {
            this.Id = id;
            this.Nome = nome;
        }
        public Cliente()
        {
            this.Id = -1;
            this.Nome = string.Empty;
        }
        public void ImprimeID()
        {
            WriteLine($"ID = {this.Id}");
        }
        public void ImprimeNome()
        {
            WriteLine($"Nome = {this.Nome}");
        }
        public string GetNomeCompleto(string Nome, string Sobrenome)
        {
            return Nome + " " + Sobrenome;
        }
    }
}

Refatore o código do projeto definindo dois métodos :

No método ReflectionEarlyBinding() copie e cole o código existente no método Main().

No método ReflectionLateBinding() inclua o código a seguir:

        static void ReflectionLateBinding()
        {
            // Carrega o assembly atual em execução como a classe Cliente
            Assembly assemblyEmExecucao = Assembly.GetExecutingAssembly();
            // Carrega a classe CLiente para qual criamos a instância dinâmica
            Type tipoCliente = assemblyEmExecucao.GetType("CShp_Reflection.Cliente");
            
            // Cria a instância do tipo Cliente usando a classe Activator
            object instanciaCliente = Activator.CreateInstance(tipoCliente);            
            // Obtém informação de método usando o tipoCliente e GetMethod()
            MethodInfo getNomeCompleto = tipoCliente.GetMethod("GetNomeCompleto");

            // Cria um array de parâmetros e preenche com nome e sobrenome
            string[] metodoParametros = new string[2];
            metodoParametros[0] = "Macoratti";  //nome
            metodoParametros[1] = ".net";           //sobrenome            
            // Invoca o método passando a instanciaCliente e os parâmetros
            string nomeCompleto = (string)getNomeCompleto.Invoke(instanciaCliente, metodoParametros);
            WriteLine($"Nome Completo = {nomeCompleto}");
        }

Nesta classe temos :

1- A carga do assembly que contém a classe Cliente; no nosso caso, a classe Cliente esta presente no mesmo assembly que a classe Main, assim usamos: Assembly.GetExecutingAssembly(); para carregar o assembly em execução. Na classe Assembly existem diversos métodos estáticos que podemos usar para carregar um assembly em tempo de execução

2- A seguir carregamos a classe Cliente usando:  assemblyEmExecucao.GetType("CShp_Reflection.Cliente");  Note que passamos o nome qualificado para o método GetType();

3- Criamos uma instância da classe Cliente dinamicamente usando : Activator.CreateInstance(tipoCliente);

Nota: A classe Activator permite criar tipos e instâncias usando late binding.

4- Agora que temos uma instância da classe Cliente podemos obter informação do método 'GetNomeCompleto' usando : tipoCliente.GetMethod("GetNomeCompleto");

5- O método GetNomeCompleto espera por dois parâmetros do tipo string, assim, criamos um array de strings e preenchemos com os valores para nome e sobrenome;

6- Finalmente invocamos o método passando a instância e array de parâmetros.

O código completo da classe Program após a refatoração ficou assim:

using System;
using System.Reflection;
using static System.Console;
namespace CShp_Reflection
{
    class Program
    {
        static void Main(string[] args)
        {
            //ReflectionEarlyBinding();
            ReflectionLateBinding();
            ReadLine();
        }
        static void ReflectionLateBinding()
        {
            // Carrega o assembly atual em execução como a classe Cliente
            Assembly assemblyEmExecucao = Assembly.GetExecutingAssembly();
            // Carrega a classe CLiente para qual criamos a instância dinâmica
            Type tipoCliente = assemblyEmExecucao.GetType("CShp_Reflection.Cliente");
            
            // Cria a instância do tipo Cliente usando a classe Activator
            object instanciaCliente = Activator.CreateInstance(tipoCliente);
            
            // Obtém informação de método usando o tipoCliente e GetMethod()
            MethodInfo getNomeCompleto = tipoCliente.GetMethod("GetNomeCompleto");
            
            // Cria um array de parâmetros e preenche com nome e sobrenome
            string[] metodoParametros = new string[2];
            metodoParametros[0] = "Macoratti";  //nome
            metodoParametros[1] = ".net";          //sobrenome
            
            // Invoca o método passando a instanciaCliente e os parâmetros
            string nomeCompleto = (string)getNomeCompleto.Invoke(instanciaCliente, metodoParametros);
            WriteLine($"Nome Completo = {nomeCompleto}");
        }
        static void ReflectionEarlyBinding()
        {
            // Obtém o Tipo usando o método estático GetType()
            Type T = Type.GetType("CShp_Reflection.Cliente");
            // Imprime os detalhes do Tipo
            WriteLine($"Nome Completo = {T.FullName}");
            WriteLine($"Apenas o nome da Classe = {T.Name}");
            WriteLine($"Apenas o namespace = {T.Namespace}");
            WriteLine();
            // Imprime a lista de métodos
            WriteLine("Metodos na classe Cliente");
            MethodInfo[] methods = T.GetMethods();
            foreach (MethodInfo method in methods)
            {
                // Imprime o tipo de Retorno e o nome do método
                WriteLine(method.ReturnType.Name + " " + method.Name);
            }
            WriteLine();
            //  Imprime as Propriedades
            WriteLine("Propriedades na classe Cliente");
            PropertyInfo[] properties = T.GetProperties();
            foreach (PropertyInfo property in properties)
            {
                // Imprime o tipo e nome da propriedade
                WriteLine(property.PropertyType.Name + " " + property.Name);
            }
            WriteLine();
            //  Imprime os construtores
            WriteLine("Construtores na classe Cliente");
            ConstructorInfo[] constructors = T.GetConstructors();
            foreach (ConstructorInfo constructor in constructors)
            {
                WriteLine(constructor.ToString());
            }
        }
    }
}

Vimos assim as duas formas de obter informações de uma classe usando Reflection usando Early Binding e Late Binding.

A ligação tardia ou late binding é útil para ler as informações dos metadados de um assembly quando o tipo não é conhecido.

Pegue o projeto completo aqui:  CShp_Reflection.zip

"E estava ali um homem que, havia trinta e oito anos, se achava enfermo.Jesus disse-lhe: Levanta-te, toma o teu leito, e anda.Logo aquele homem ficou são; e tomou o seu leito, e andava. E aquele dia era sábado."
João 5:5-9

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Referências:


José Carlos Macoratti