Este é um post disponível para assinantes MVPReflection - Artigo .net Magazine 84
Este artigo fala sobre Reflection, uma técnica para observar e modificar a estrutura do código executável em tempo de execução. Explica a implementação no .NET, falando sobre Introspection, IL Emitting e ferramentas análogas, como Mono.Cecil e FxCop Introspection Model.
[Artigo já está disponível no Leitor Digital DevMedia®. Clique aqui para acessá-lo]
> Clique aqui para ler todos os artigos da .net Magazine 84
Você sabe o que acontece quando um programa é executado? No C# e em diversas outras linguagens, o código-fonte precisa ser compilado para poder ser executado. Isto é, ele precisa ser transformado em uma representação mais amigável à máquina. Enquanto está executando, os programas compilados ficam carregados na memória, assim como os dados que possuem. Código e dados compartilham o mesmo espaço na memória. Sendo assim, pergunta-se: seria possível manipular o código em tempo de execução tal qual é possível com os dados?
A resposta é: sim! Por muito tempo, análise e modificação do código em tempo de execução têm sido utilizadas pelos programadores para dar características dinâmicas em seus programas. Entretanto, essas técnicas envolviam manipulação de endereços de memória de forma crua, o que geralmente podia levar a bugs indecifráveis e a sérias falhas de segurança.
Conforme as linguagens foram evoluindo, mecanismos foram sendo desenvolvidos para representar o código executável de forma estruturada, permitindo que um programa pudesse mais facilmente analisar sua própria representação através de estruturas de alto nível, reduzindo muito os possíveis bugs. Esses mecanismos definem o que se conhece por Reflection (ou Reflexão). Para termos de nomenclatura, quando essa representação abrange o código executável, denomina-se reflexão computacional; quando abrange as estruturas definidas no programa (como classes e métodos), é chamada reflexão estrutural.
Como citado, o .NET suporta reflexão estrutural amplamente através do namespace System.Reflection e, de uma forma mais tímida, também suporta reflexão computacional através de IL Emitting. Esse suporte é padronizado na plataforma para que seja possível analisar a estrutura de um programa independente da linguagem na qual ele foi escrito. Isto é, os metadados de um programa escrito em VB.NET, por exemplo, podem ser lidos por um programa escrito em C#, de forma transparente. Isso é crucial para permitir a interoperabilidade das linguagens.
Reflection está presente em muitos pontos da BCL (Base Class Librarys) e em muitas bibliotecas e frameworks do mercado. ORMs como NHibernate e Entity Framework usam reflection para alterar o estado de objetos em tempo de execução. Bibliotecas como Castle.DynamicProxy e a System.Xml.Serialization usam IL Emitting para gerar código em tempo de execução, seja com o objetivo de aumentar a performance ao longo tempo, ou até mesmo para estender a funcionalidade de certos objetos.
Reflection ou Introspection?
Existe muita discussão com relação a nomenclatura no assunto. Algumas fontes dizem que Reflection é a capacidade de analisar e alterar o programa em tempo de execução, e que a análise somente é chamada de introspection. Outras fontes dizem que isso é irrelevante e que introspection, na verdade, tem a ver com a capacidade de um objeto conhecer seu próprio tipo dinamicamente. Será assumida a segunda definição para este artigo.
O .NET é capaz de introspection através do método GetType, presente na classe object e herdado por todos os outros objetos. Ele retorna uma instância da classe Type, porta de entrada para a maior parte das manipulações usando reflection. As estruturas principais do .NET estão representadas por classes específicas no namespace System.Reflection. A Figura 1 mostra algumas das principais classes desse namespace.
Cada uma dessas classes permite obter informações sobre as estruturas de um programa. Uma instância de Type, por exemplo, possui o GetMethods, que é capaz de enumerar os métodos públicos de um certo tipo. A classe PropertyInfo permite que obtenha-se ou altere-se o valor de uma propriedade em um objeto dinamicamente. Exemplos práticos serão discutidos a frente.
IL Emitting
Outra técnica importante de reflection é o uso das classes do namespace System.Reflection.Emit. Com ele, o programa é capaz de gerar classes e assemblies em tempo de execução, opcionalmente salvando em disco. O conjunto de classes desse namespace foi feito especialmente para ser usado por compiladores e script engines, mas tem grande utilidade em aplicações e bibliotecas comuns, pois por MSIL ser a linguagem mais básica da plataforma .NET, é possível alcançar grande flexibilidade ao escrever o código.
As principais classes usadas para este propósito são:
· AssemblyBuilder - usada para criar assemblies em runtime;
· DynamicMethod - permite criar um método descartável que pode ser recolhido pelo Garbage Collector;
· ILGenerator - usada para efetivamente emitir código executável;
Dentre essas, uma das classes que merece destaque é a DynamicMethod. Ela foi introduzida no .NET 2.0 e permite que métodos muito leves sejam criados em tempo de execução. Esses métodos podem ser chamados através de delegates com a mesma performance de um método normal. A principal vantagem desses métodos é a possibilidade de serem coletados pelo Garbage Collector.
Analogamente, o .NET Framework 4.0 introduziu o conceito de Collectible Assemblies. Até então, todo assembly carregado não podia ser descarregado até a finalização do AppDomain. Agora, um assembly criado a partir de IL Emitting pode ser marcado como "coletável" e ele poderá ser descartado após o uso. Isso evita que o consumo de memória da sua aplicação cresça indefinidamente ao utilizar bibliotecas que geram assemblies em runtime, como a System.Xml.Serialization.
ATENÇÃO! A exibição deste artigo foi interrompida.
Este é um post disponível para assinantes MVP
Space do autor



1
0
