Back-End

23 dez, 2016

C# – Usando Reflection na prática – Parte 01

Publicidade

Neste artigo, vou mostrar uma aplicação prática da utilização de Reflection: Gerando um log de auditoria.

Como eu já apresentei o Reflection em outros, artigos vou iniciar com um pequeno resumo e depois partir para a prática.

Usando Reflection, podemos inspecionar o código em tempo de execução (runtime) e realizar tarefas como:

  • Obter metadados das propriedades e métodos;
  • Instanciar objetos;
  • Chamar métodos e alterar propriedades;
  • Compilar e executar código dinamicamente.

Dessa forma, podemos usar Reflection para realizar uma programação genérica, na qual podemos generalizar o código onde os nomes das propriedades e métodos podem variar.

Então é isso que vou mostrar neste artigo.

Vamos imaginar um cenário em que precisamos logar as informações das nossas classes de domínio em um projeto.

Temos as classes Cliente, Produto e Pedido e precisamos logar as informações.

Vou mostrar como podemos fazer isso sem usar Reflection e depois como o uso de Reflection vai facilitar a nossa vida.

Recursos usados:

Nota: Baixe e use a versão Community 2015 do VS; ela é grátis e é equivalente à versão Professional.

Criando a solução no VS 2015 Community

Abra o Visual Studio Community 2015 e clique em New Project.

Selecione Visual C# -> Console Application.

Informe o nome UsandoReflection e clique no botão OK.

Criando as classes

Vamos criar as classes Cliente, Produto e Pedido via menu Project Add Class.

A seguir temos o código de cada classe:

1 – Cliente

   public class Cliente
    {
        public int Id { get; set; }
        public string Nome { get; set; }
        public string Endereco { get; set; }
    }

2 – Produto

    public class Produto
    {
        public int Id { get; set; }
        public string Nome { get; set; }
        public string Descricao { get; set; }
        public Decimal Preco { get; set; }
        public int Estoque { get; set; }
    }

3 – Pedido

    public class Pedido
    {
        public int Id { get; set; }
        public int ClienteId { get; set; }
        public DateTime DataPedido { get; set; }

    }

Criando a classe de Log sem usar Reflection

Agora vamos fazer uma implementação bem simples da classe que vai logar as informações de que precisamos.

No menu Project, clique em Add Class e informe nome LogSemReflection; a seguir, inclua o código abaixo nessa classe:

using System;
using System.Text;

namespace UsandoReflection
{
    public class LogSemReflection
    {
        public static void LogProdutos(Produto produto)
        {
            StringBuilder builder = new StringBuilder();
            builder.AppendLine("Log do produto");
            builder.AppendLine("Data: " + DateTime.Now);
            builder.AppendLine("Id: " + produto.Id);
            builder.AppendLine("Nome: " + produto.Nome);
            builder.AppendLine("Descrição: " + produto.Descricao);
            builder.AppendLine("Estoque: " + produto.Estoque);
            ImprimeLog(builder.ToString());
            //SalvaLog()
        }

        public static void LogPedidos(Pedido pedido)
        {
            StringBuilder builder = new StringBuilder();
            builder.AppendLine("Log do pedido");
            builder.AppendLine("Data: " + DateTime.Now);
            builder.AppendLine("Id: " + pedido.Id);
            builder.AppendLine("ClienteId: " + pedido.ClienteId);
            builder.AppendLine("DataPedido: " + pedido.DataPedido);
            ImprimeLog(builder.ToString());
        }

        public static void LogClientes(Cliente cliente)
        {
            StringBuilder builder = new StringBuilder();
            builder.AppendLine("Log do cliente");
            builder.AppendLine("Data: " + DateTime.Now);
            builder.AppendLine("Id: " + cliente.Id);
            builder.AppendLine("Nome: " + cliente.Nome);
            builder.AppendLine("Endereço: " + cliente.Endereco);
            ImprimeLog(builder.ToString());
        }

        public static void ImprimeLog(string texto)
        {
            Console.WriteLine(texto);
        }

        public void SalvaLog(string texto)
        {
            //salva o log
        }
    }
}

Definimos três métodos, um para logar as informações de cada classe. Assim, temos os métodos:

  1. LogProdutos
  2. LogClientes
  3. LogPedidos

Essa abordagem é bem ingênua, mas é mais comum do que se imagina. Nela, para cada classe que desejamos logar, vamos ter que criar um novo método na classe LogSemReflection.

Não precisa ser muito esperto para perceber que essa classe fere os princípios das boas práticas da programação e não está de acordo com os princípios SOLID.

A implementação não respeita o princípio da responsabilidade única (SRP), nem o princípio aberto fechado (Open/Closed) – sSem dizer que estamos repetindo código ferindo o principio DRY.

Então vamos melhorar o código usando Reflection.

Criando a classe de Log usando Reflection

Agora vamos fazer uma implementação do log usando Reflection e melhorar o nosso código tornando-o mais robusto e aderente às boas práticas.

No menu Project, clique em Add Class e informe nome LogComReflection; a seguir, inclua o código abaixo nessa classe:

using System;
using System.Text;

namespace UsandoReflection
{
    public class LogComReflection
    {
        //método genérico para criar um log 
        //para qualquer classe
        public static void Log(object obj)
        {
            //obtem o tipo do objeto
            //esse tipo não tem relação com a instância de obj
            var tipo = obj.GetType();

            StringBuilder builder = new StringBuilder();
            //obtem o nome do tipo
            builder.AppendLine("Log do " + tipo.Name);
            builder.AppendLine("Data: " + DateTime.Now);

            //Vamos obter agora todas as propriedades do tipo
            //Usamos o método GetProperties para obter 
            //o nome das propriedades do tipo
            foreach (var prop in tipo.GetProperties())
            {
                //usa a propriedade Name para obter o nome da propriedade
                //e o método GetValue() para obter o valor da instância desse tipo
                builder.AppendLine(prop.Name + ": " + prop.GetValue(obj));
            }
            ImprimeLog(builder.ToString());
        }

        public static void ImprimeLog(string texto)
        {
            Console.WriteLine(texto);
        }
    }
}

Agora temos um único método genérico que pode ser usado para logar as informações de qualquer classe.

Logando as informações

Apenas para mostrar que o código está funcional, vamos implementar o código que cria instâncias das classes e usa as duas implementações do log.

Abra o arquivo Program e inclua o código a seguir:

using System;

namespace UsandoReflection
{
    class Program
    {
        static void Main(string[] args)
        {
            var cliente = new Cliente()
            {
                Id = 10,
                Nome = "Macoratti",
                Endereco = "Rua Projetada, 100"
            };

            var produto = new Produto()
            {
                Id = 1,
                Nome = "Caderno",
                Descricao = "Caderno Espiral 100 folhas",
                Estoque = 100,
                Preco = 3.99M
            };

            var pedido = new Pedido()
            {
                Id = 1,
                ClienteId = 1,
                DataPedido = DateTime.Now
            };

            Console.WriteLine("***** Logando sem usar Reflection ****");
            LogarSemReflection(cliente, produto, pedido);
            Console.WriteLine(" ---------- Logando usando Reflection ----------");
            LogarUsandoReflection(cliente, produto, pedido);
            Console.ReadKey();
        }

        public static void LogarSemReflection(Cliente cli, Produto prod, Pedido ped)
        {
            LogSemReflection.LogClientes(cli);
            LogSemReflection.LogProdutos(prod);
            LogSemReflection.LogPedidos(ped);
        }

        public static void LogarUsandoReflection(Cliente cli, Produto prod, Pedido ped)
        {
            LogComReflection.Log(cli);
            LogComReflection.Log(prod);
            LogComReflection.Log(ped);
        }

    }
}

c_reflec11

Executando o projeto, teremos o resultado mostrado na figura acima.

Na segunda parte do artigo, vou mostrar outra aplicação prática de Reflection.

Pegue o projeto completo aqui: UsandoReflection.zip.