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:

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!

[caption id="attachment_299" align="aligncenter" width="691"]Figura 1 - Criando o projeto Console Application. Figura 1 - Criando o projeto Console Application.[/caption]

[sourcecode language="csharp"]
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; }
}
}
[/sourcecode]

[sourcecode language="csharp"]
public static string Serialize(object obj)
{
var strSerializada = "";
}
[/sourcecode]

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.

[sourcecode language="csharp"]
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 + "}";
}
[/sourcecode]

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.

[caption id="attachment_302" align="aligncenter" width="660"]Figura 2 - Classe Type implementando a interface IReflect. Figura 2 - Classe Type implementando a interface IReflect.[/caption]

[caption id="attachment_303" align="aligncenter" width="660"]Figura 3 - Contrato do método GetProperties na interface IReflection. Figura 3 - Contrato do método GetProperties na interface IReflection.[/caption]

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.

[sourcecode language="csharp"]
static void Main(string[] args)

{

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

}[/sourcecode]

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

[caption id="attachment_304" align="aligncenter" width="660"]Figura 4 - Retorno do método Serialize. Figura 4 - Retorno do método Serialize.[/caption]

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

[caption id="attachment_305" align="aligncenter" width="660"]Figura 9 - Utilizando um validado json. Figura 5 - Utilizando um validado json.[/caption]

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