C# Avançado – Introdução à Reflexão
A plataforma Microsoft .NET trouxe uma série de novas e poderosas formas de desenvolver software. Entre todas as inovações, creio que as maiores são os atributos e a reflexão. A reflexão consiste em ler os dados de um assembly .NET em tempo de execução.
Quando seu código é compilado, ele é traduzido para uma linguagem intermediária, chamada Microsoft Intermediate Language (MSIL), também conhecida como Common Intermediate Language (CIL). Este código em linguagem intermediária é gravado em forma binária em um arquivo de extensão DLL ou EXE. A este arquivo chamamos assembly. Isto posto, a reflexão nada mais é do que a capacidade que uma aplicação .NET tem de ler um assembly e obter informações sobre ele: as classes, os métodos e seus parâmetros, as propriedades, eventos e demais dados sobre o código que está contido no assembly.
Mas como lemos estas informações?
Bem, para ler os dados de um assembly, a primeira coisa que temos que fazer é carregá-lo. O exemplo abaixo grava em uma variável uma referência para o assembly do próprio programa (ou seja, seu executável):
// Nesta linha, gravamos o assembly atual em uma variável
Assembly assembly = Assembly.GetExecutingAssembly();
Quando fazemos isto, podemos utilizar todas as funcionalidades que a classe System.Reflection.Assembly nos proporciona. Veja o exemplo abaixo:
using System;
using System.Reflection;
using System.IO;
namespace ConsoleApplication1
{
public class Program
{
static void Main(string[] args)
{
// Grava o caminho para o arquivo do assembly
// neste caso, é a própria aplicação, mas pode ser qualquer
// assembly .NET
string assemblyPath = Path.Combine(
Environment.CurrentDirectory, "ConsoleApplication1.exe");
// Nesta linha gravamos o assembly em uma variável:
Assembly assembly = Assembly.LoadFile(assemblyPath);
// agora estamos carregando o tipo Test que está dentro do assembly
// e gravando-o na variável “type”.
Type type = assembly.GetType("ConsoleApplication1.Test");
// Agora criamos uma instância do tipo Test
object test = Activator.CreateInstance(type);
// espera para termos tempo de ler a saída
Console.ReadLine();
}
}
public class Test
{
public Test()
{
Console.Write("Olá, sou do tipo Test e fui criado por reflexão.");
}
}
}
Execute a aplicação e você deverá receber a seguinte saída:
Olá, sou do tipo Test e fui criado por reflexão.
É verdade que este código não é muito útil, por que seria muito mais fácil fazer:
Test test = new Test();
Entretanto, a diferença é que por reflexão, podemos abrir qualquer assembly sem que nosso código tenha referências diretas a ele, eliminando, desta forma, a dependência entre as camadas.
Agora, vamos fazer algo mais útil:
using System;
using System.Collections.Generic;
using System.Reflection;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
// Vamos gravar o tipo da classe Test em uma variável
Type type = typeof(Test);
// agora, vamos gravar uma referência para um método privado
MethodInfo method = type.GetMethod(
"PrivateMethod",
BindingFlags.Static | BindingFlags.NonPublic
);
// agora, vamos chamá-lo
method.Invoke(null, null);
// e vamos esperar o usuário digitar uma tecla
Console.ReadLine();
}
}
public class Test
{
private static void PrivateMethod()
{
Console.WriteLine("Sou um método privado");
}
}
}
Como se pode esperar, a saída é:
Sou um método privado
O código acima faz algo definitivamente não-trivial: chama um método privado de uma classe desde outra classe. É certo que há poucas situações onde temos que fazer isto (unit tests, por exemplo). De qualquer forma, este exemplo pode mostrar o poder da reflexão.
Conclusão
A reflexão é a arma mais poderosa que a plataforma Microsoft .NET pode nos oferecer. Com ela, podemos criar uma instância de um objeto tendo apenas o nome da classe e o caminho do arquivo do assembly. Também por reflexão, podemos chamar um método privado a partir de outra classe (algo que é simplesmente impossível sem utilizar esta técnica).