C# – Reflection

Fala pessoal, tudo bom?

Nesse post falarei sobre um recurso que o .NET Framework oferece e que em algumas situações me ajudou a desenvolver funcionalidades que precisavam saber como era a estrutura de uma classe em tempo de execução. Para fazer isso, usei reflection.

Introdução

Todo desenvolvedor um dia precisará percorrer as propriedades de um objeto por algum motivo. Já passei por isso algumas vezes e se você ainda não passou, mais cedo ou mais tarde, chegará sua vez.

Quando uma aplicação está em execução, nós já não temos mais nosso código c#, vb, etc., basicamente o que temos é linguagem intermediária (IL) e metadados que todo compilador de linguagem .NET gera, tenho uma serie de publicações onde você pode ler um pouco sobre esse assunto. Bom, sendo assim, como fazer para ler a definição de uma classe em tempo de execução? Usando reflection!

System.Reflection

O namespace System.Reflection da FCL contém várias classes que implementam métodos que são capazes de refletir metadados através de objetos, com isso, podemos, em tempo de execução, saber quais propriedades uma classe possui, os tipos dessas propriedades, também conseguimos consultar quais os eventos e interfaces que uma classe implementa, etc.  Vamos ver na prática como isso funciona.

Percorrendo as propriedades de uma classe

Aqueles que nunca precisaram percorrer as propriedades de uma classe provavelmente estão se perguntando “pra que eu precisaria fazer isso?” Algumas situações que eu precisei fazer isso foram:

  • Criar query’s dinamicamente;
  • Serializar dados.

Precisei percorrer propriedades em tempo de execução para esses dois casos. Outro exemplo é o Entity Framework, que em alguns momentos utiliza reflection para criar classes em tempo de execução, você pode ver o código do Entity Framework aqui. Para mostrar um exemplo prático sobre reflection vou seguir o segundo caso, serializar dados. Em tempo de execução vamos serializar os dados de um objeto em json, formatando datas para o formato dd/mm/yyyy e decimais/doubles com duas casas após a virgula. Antes de iniciarmos o exemplo, vale lembrar que o que vamos fazer tem somente finalidade didática, pois, existem várias ferramentas que já fazem isso, uma delas é o newtonsoft.json. Mãos a obra!

  • No Visual Studio, crie um novo projeto do tipo ConsoleApplication.
Figura 1 - Criando o projeto Console Application.

Figura 1 – Criando o projeto Console Application.

  • Crie uma classe publica chamada Produto com algumas propriedades.
using System;

namespace ExemploReflection
{
  public class Produto
  {
    public int IdProduto { get; set; }
    public string Nome { get; set; }
    public decimal Preco { get; set; }
    public DateTime DataCadastro { get; set; }
    public bool Ativo { get; set; }
  }
}
  • Após criar a classe Produto, volte para a classe Program e crie um método chamado Serialize.
public static string Serialize(object obj)
{
  var strSerializada = "";
}

O método Serialize será o responsável por formatar e serializar os valores das propriedades de uma classe que será enviada por argumento. Um ponto a ser observado no método Serialize é o tipo do seu parâmetro, object. No .Net Framework, todos os tipos herdam de object. Nesse caso, usaremos o tipo object para que o método Serialize seja genérico. O método Serialize não precisa saber qual o tipo que será serializado, o que o método precisa saber é quais são os tipos das propriedades da classe do parâmetro, isso será feito em tempo de execução. Vamos agora finalizar o método Serialize e de fato percorrer as propriedades de uma classe.

public static string Serialize(object obj)
{
  var strSerializada = "";
  foreach (var prop in obj.GetType().GetProperties())
    {
      var valor = prop.GetValue(obj);
      if (prop.PropertyType == typeof (DateTime))
        {
          strSerializada += "\"" + prop.Name + "\":" + "\"" + String.Format("{0:dd/MM/yyyy}", valor) + "\",";
        }
      else if (prop.PropertyType == typeof (Decimal) || prop.PropertyType == typeof (Double))
        {
          strSerializada += "\"" + prop.Name + "\":" + "\"" + String.Format("{0:N}", valor) + "\",";
        }
      else
        {
          strSerializada += "\"" + prop.Name + "\":" + "\"" + valor + "\",";
        }
    }

  strSerializada = strSerializada.Substring(1, strSerializada.Length - 2);

  return "{\"" + strSerializada + "}";
}

Analisando o método Serialize:

3ª linha: declarando a variável strSerializada, nessa variável montaremos um objeto json a partir do objeto que recebermos por parâmetro.

4ª linha: Inicio de um laço que percorrerá todas as propriedades de objeto recebido por parâmetro. O método GetType() retorna o tipo de um objeto e o método GetProperties() retorna um Array do tipo PropertyInfo, o tipo PropertyInfo faz parte do namespace System.Reflection. Pode ser que tenha surgido a seguinte dúvida: “Como estamos usando o tipo PropertyInfo sendo que nem referenciamos o namespace ao qual ele faz parte?” Bom, isso foi possível porque o retorno do método GetType() é um Type e o tipo Type implementa a interface IReflect que por sua vez possuí o contrato do método GetProperties(), veja isso nas Figuras 2 e 3.

Figura 2 - Classe Type implementando a interface IReflect.

Figura 2 – Classe Type implementando a interface IReflect.

Figura 3 - Contrato do método GetProperties na interface IReflection.

Figura 3 – Contrato do método GetProperties na interface IReflection.

6ª linha: Estamos criando a variável valor e atribuindo o valor da propriedade em questão no laço. Para recuperar o valor da propriedade foi utilizado o método GetValue() que faz parte do tipo PropertyInfo.

7ª a 18º linha: Nesse trecho de código está sendo feito algumas validações usando if/else. O método PropertyType retorna o tipo de uma propriedade. A função typeof retorna o tipo de um objeto. Quando uma propriedade é do tipo DateTime está sendo formatada para “dd/mm/yyyy” e quando uma propriedade é do tipo decimal ou double está sendo formatada para mostrar duas casas decimais. O método Name retorna uma string com o nome da propriedade. O restante são concatenações básicas. Repare nos trechos onde aparecem “\’’”, é preciso colocar a “\” quando queremos colocar aspas dentro de uma string.

21ª linha: Truncando a string strSerializada retirando o último caractere que é uma virgula “,”.

23ª linha: Retornando a string StrSerializada dentro de colchetes “{}”, um objeto json inicia com “{” e é finalizado com “}”

No método Main vamos chamar o método Serialize passando como argumento um objeto Produto.


static void Main(string[] args)

{

Serialize(new produto{ IdProduto = 1, Nome = "Notebook", Preco = 1200, DataCadastro = DateTime.Now, Ativo = true});

}

Veja na Figura 4 o retorno do método Serialize.

Figura 4 - Retorno do método Serialize.

Figura 4 – Retorno do método Serialize.

Utilizando um validador de json, vemos que o retorno é um json válido, como mostra a Figura 5.

Figura 9 - Utilizando um validado json.

Figura 5 – Utilizando um validado json.

Bom, por hoje era isso, uma visão superficial de reflection usando C#.

Até o próximo post!

Referências

CLR via C# – Jeffrey Richter

6 comentários sobre “C# – Reflection

  1. Olá Wennder!
    Parabéns pelo artigo! Realmente Reflection é um tema importantíssimo para qualquer desenvolvedor .NET. Principalmente quando utilizamos bibliotecas de terceiros, muitas vezes para solucionarmos uns problemas cabeludos o único jeito é usar reflection pra chamar um método privado ou setar algum atributo que a biblioteca resolveu não expor..
    Abraço!
    André Lima

    • Fala André, blz?
      Obrigado pela participação!
      O ponto que você levantou é interessantíssimo, eu ainda não precisei usar reflection para solucionar algum problema ao usar bibliotecas de terceiros, por coincidência, há alguns minutos atrás eu estava conversando exatamente sobre isso com um colega que leu o post e também citou esse ponto hehehe. Acho que vale um post seu sobre esse tipo de situação heim 🙂

      Abraços!

      Wennder Santos

  2. Parabéns pelo artigo.

    Reflection é um recurso pouco explorado por autores de artigos, mas necessário em determinadas situações. Eu por exemplo, usei Reflection num repositório genérico para o Entity Framework.

    Mostrar como serializar dados para o formato json é mais um ponto positivo para o artigo.

    • Olá Alfredo, tudo bom?
      Obrigado pelo feedback. Eu vi em alguns lugares o uso de reflection para a mesma finalidade que você citou, muito legal, inclusive o próprio entity usa reflection. Tentei ser didático no exemplo que escolhi, espero ter conseguido.

      Abraços.

      Wennder Santos

Deixar mensagem para wenndersantos Cancelar resposta