Exploração da reflexão na plataforma .NET

Introdução

O espaço chamado "System.Reflection"do Framework .NET nos permite dissecar uma Assembly (uma Assembly é um projeto) e saber por exemplo: qual é a lista de campos, propriedades ou métodos que contêm classes desta montagem (Assembly).

Pré-requisito

Vamos supor que nossos leitores têm um mínimo de conhecimento sobre a plataforma .NET e os WinForms. Além do mais, vamos tomá-los como base no nosso exemplo sobre o Framework.NET 3.5 com o IDE Microsoft Visual Studio 2008.

Criação do projeto e elementos a serem explorados

Abaixo, nossas escolhas para a criação do nosso projeto.

Criação do projeto

Começaremos por escolher um projeto do tipo "Windows Forms Application". Aqui, vemos a escolha do ". NET Framework 3.5".

Depois de criar o projeto, você encontrará um WinForms já adicionado "Form1".

Agora adicione uma classe ao seu projeto "Shift + Alt + c"

Na Classe1 adicionar os seguintes campos:

 private string camposTeste1 = string.Empty; 
        private string champsTest2 = string.Empty;

E as seguintes propriedades:

  public string CamposTeste1 
        { 
            get { return camposTeste1; } 
            set { camposTeste1 = value; } 
        } 

        public string CamposTeste2 
        { 
            get { return camposTeste2; } 
            set { camposTeste2 = value; } 
        }

E os seguintes métodos:

   public string Method1() 
        { 
            return string.Empty; 
        } 

        public string Method2() 
        { 
            return string.Empty; 
        } 

Agora vá em Form1, adicione um botão, e insira o seguinte código em seu evento clique:

private void button1_Click(object sender, EventArgs e) 
        { 
            // Em primeiro lugar, fazemos uma chamada em curso de execução 
            Assembly asm = Assembly.GetExecutingAssembly(); 
            // Vamos escolher a classe Classe1 para dissecá-la, indo buscar seu tipo  
            System.Type type = asm.GetType("Test.Class1"); 

            // pedimos ao Tipo de nos retornar a lista dos campos que ele contém  

            string strFields = "Lista dos fields : rn"; 
            foreach (FieldInfo fi in type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)) 
                strFields += fi.Name + " : " + fi.FieldType.Name + ", "; 

            // pedimos ao Tipo de nos retornar a lista dos métodos que ele contém  
            string strMethods = "rnrn Lista dos métodos : rn"; 
            foreach (MethodInfo mi in type.GetMethods()) 
                strMethods += mi.Name + " : " + mi.ReturnType.Name + ", "; 

            // pedimos ao Tipo de nos retornar a lista das propriedades que ele contém  
            string strProperties = "rnrn Lista das propriedades: rn"; 
            foreach (PropertyInfo pi in type.GetProperties()) 
                strProperties += pi.Name + " : " + pi.PropertyType.Name + ", "; 

            MessageBox.Show(strFields + strMethods + strProperties); 
        }

Lembre-se de adicionar a seguinte declaração no seu formulário:

using System.Reflection;

Execute o seu projeto e pressione o botão do seu formulário: você verá uma lista dos campos, propriedades e métodos que contém a classe Class1. O mais importante é que todo o processo é desenvolvido de uma forma genérica, em nenhum caso, fomos obrigados a instanciar nossa classe e obter estas propriedades, esses campos ou esses métodos de uma forma convencional.

Uso da reflexão

Suponhamos que utilizamos o Class1 como o apoio de dado para depois inserí-lo em um banco de dados e, queremos ter certeza de que na validação desta inserção, todas as propriedades do Class1, que são obrigatórias, terão um valor, só que queremos fazer esta verificação de forma genérica. Para isso, vamos utilizar o espaço de nome (namespace) "System.Reflection" e o espaço de nome "System.ComponentModel", que nos permitirá de adicionar dados estáticos à nossa classe a partir dos atributos.

Em seu Class1 adicione a seguinte instrução:

using System.ComponentModel;

E substitua as duas propriedades da sua classe pelas seguintes propriedades:

[Description("descrição dos camposTeste1")] 
        public string CamposTeste1 
        { 
            get { return camposTeste1; } 
            set { camposTeste1 = value; } 
        } 

        [Description("descrição dos camposTeste 2"), Category("Mandatory")] 
        public string CamposTeste2 
        { 
            get { return camposTeste2; } 
            set { camposTestet2 = value; } 
        }

Você pode ver que adicionamos atributos às nossas propriedades: o atributo "Descrição" (Description] e o atributo "Categoria" (Category]. Agora, vamos supor que todas as propriedades que possuem um atributo "Categoria" com valor "Mandatory" devem, necessariamente, ter um valor diferente de zero.

A atribuição de atributos pode ser feita para as classes, propriedades, campos e métodos; em outro caso de uso, podemos criar o nosso próprio atributo (por exemplo: criar um atributo "Persistir"), que será distribuído às propriedades do uma classe e cujo valor servirá a identificar o campo em que o banco de dados vai inserir o valor de nossa propriedade, e a partir daí, poderemos gerar pedidos de inserção de maneira completamente automatizada.

Agora vá em Form1, adicione um botão, e insira o seguinte código em seu evento clique:

 private void button2_Click(object sender, EventArgs e) 
        { 
            // Em primeiro lugar, fazemos uma chamada em curso de execução  
            Assembly asm = Assembly.GetExecutingAssembly(); 
            // Vamos escolher a classe Classe1 para dissecá-la, indo buscar seu tipo
            System.Type type = asm.GetType("Test.Class1"); 
            // podemos utilizar a reflexão para instanciar uma classe  
            object o = asm.CreateInstance("Test.Class1"); 
            // podemos utilizar a reflexão para modificar o valor de uma propriedade 
            PropertyInfo pi = type.GetProperty("ChampsTest1"); 
            pi.SetValue(o, "novo valor", null); 
            // podemos utilizar a reflexão para modificar o valor de uma propriedade
            pi = type.GetProperty("CamposTeste2"); 
            pi.SetValue(o, null, null); 

            // pedimos ao Tipo de nos retornar a lista das propriedades que ele contém   
            string strVerification = string.Empty; 
            foreach (PropertyInfo piv in type.GetProperties()) 
            { 
                // para cada propriedade verificamos se ela possui um atributo do tipo "Category" 
                foreach(object oatt in piv.GetCustomAttributes(typeof(System.ComponentModel.CategoryAttribute),true)) 
                { 
                    // se for o caso vamos buscar sua descrição no atributo "Description" 
                    // para poder formular o nosso alerta corretamente 
                    System.ComponentModel.CategoryAttribute att =(System.ComponentModel.CategoryAttribute)oatt; 
                    if(att.Category == "Mandatory") 
                    { 
                        object[] lo = piv.GetCustomAttributes(typeof(System.ComponentModel.DescriptionAttribute), true); 
                        System.ComponentModel.DescriptionAttribute datt = (System.ComponentModel.DescriptionAttribute)lo[0]; 
                        strVerification +=  "O valor da propriedade " + datt.Description + " é obrigatório"; 
                    } 
                } 
            } 

            MessageBox.Show(strVerification); 
}

Neste exemplo, procedemos da seguinte maneira:

1 - Utilizar a reflexão para ir buscar a "Montagem", que está sendo executada através do método "GetExecutingAssembly ».

2 - Atingir um tipo desta "Montagem" graças ao método "GetType".

3 - Instanciar esta classe de forma genérica usando o método "CreateInstance".

4 - Alcançar uma propriedade desta classe usando a classe "PropertyInfo", que nos dá muitas possibilidades para manipular a nossa propriedade, como atribuir um valor através do método "SetValue "ou ler o valor da propriedade graças ao método "GetValue".

5 - Mas, sobretudo, ler os atributos das propriedades da nossa classe e descobrir quais apresentam uma obrigação de valor não-zero, gerar alertas, e tudo isso, de uma forma genérica.

Conclusão

Esta dica apresenta um primeiro contato com o espaço de nome "System.Reflection". O ponto importante é que agora é possível manipular a "Montagem" (Assembly), os "Tipos" e as "Instâncias", de modo completamente genérico.

Nosso conteúdo é produzido em colaboração com especialistas em tecnologia da informação sob o comando de Jean-François Pillou, fundador do CCM.net. CCM é um site sobre tecnologia líder em nível internacional e está disponível em 11 idiomas.
Este documento, intitulado 'Exploração da reflexão na plataforma .NET', está disponível sob a licença Creative Commons. Você pode copiar e/ou modificar o conteúdo desta página com base nas condições estipuladas pela licença. Não se esqueça de creditar o CCM (br.ccm.net) ao utilizar este artigo.

Assine nossa newsletter!

Assine nossa newsletter!
Junte-se à comunidade