Fala pessoal, tudo na paz? Peço desculpas pela demora em colocar novos posts mas as coisas andam meio corridas pro meu lado, mas para compensar isso, vamos ver hoje um post muito legal.

Hoje vamos ver como podemos criar uma classe em runtime, para isso iremos utilizar Reflection (Reflexão).

Bem, esta necessidade se deu devido a um projeto que estou trabalhando e tendo em vista que talvez alguém possa passar pelo mesmo problema, vou compartilhá-lo com vocês.

A explicação está no próprio código, caso seja necessário uma explicação mais detalhada, a colocarei no final do post ok!

Listagem 01

using System;
using System.Reflection;
using System.Reflection.Emit;

namespace DynamicProject.ConsoleApp
{
    class Program
    {
        static void Main( string[] args )
        {
            //
            // Estas constantes serão dinâmicas ou seja
            // elas virão de uma fonte totalmente desconhecida
            // mas para efeito de post, irei setá-las manualmente
            //
            const string assemblyName = "MyAssembly";
            const string moduleName = "MyModule";
            const string className = "MyClass";
            const string fieldName = "_name";
            const string propertyName = "Nome";

            //
            // Retorna o Domain da Thread corrente
            //
            AppDomain ad = AppDomain.CurrentDomain;

            // Cria um novo assembly - Namespace
            AssemblyName an = new AssemblyName
                                  {
                                      Name = assemblyName
                                  };

            //
            // Diz que nosso assembly pode apenas ser executado.
            // Temos também a opção de gravar este assembly
            // gerando uma dll, mas esse não é o nosso caso
            //
            AssemblyBuilder ab = ad.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);

            //
            // Criamos um módulo que será responsável por
            // guardar nosso assembly
            //
            ModuleBuilder mb = ab.DefineDynamicModule(moduleName);

            //
            // Nesta parte definimos qual(ais) atributo(s)
            // nossa classe irá possui. Aqui definimos que
            // nossa classe será pública e serializável
            //
            TypeBuilder tb = mb.DefineType(className, TypeAttributes.Public | TypeAttributes.Serializable);

            //
            // Como estamos utilizando reflection precisamos criar
            // um campo privado para receber os valores
            // que serão setados na propriedade, isso porque pelo que
            // andei vendo, não é possível criar propriedade automática
            // simplesmente com get/set, daí a necessidade de um private field
            //
            FieldBuilder fieldBuilder = tb.DefineField(fieldName, typeof(string), FieldAttributes.Private);

            //
            // Aqui criamos nossa Propriedade.
            // Como podem ver informamos:
            //  Nome da Propriedade
            //  Se ela vai possui um valor Default
            //  Qual o tipo (String, DateTime, Boolean, etc.)
            //  E um valor null para o ultimo parâmetro, que aliás não entendi o porque dele!!..rsrs
            //
            PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, typeof (String), null);

            //
            // Definimos alguns atributos especiais
            // para nosso field (get e set)
            //
            MethodAttributes getSetAttribute = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;

            //
            // Como não estamos utilizando propriedade automática
            // precisamos criar um método Get para nossa propriedade.
            //
            MethodBuilder custNameGetPropMthdBldr = tb.DefineMethod("get_" + propertyName, getSetAttribute, typeof(string), Type.EmptyTypes);

            //
            // Gera o MSIL que iremos trabalhar
            //
            ILGenerator custNameGetIL = custNameGetPropMthdBldr.GetILGenerator();

            //
            // Para o método get, utilizamos:
            //  OpCodes.Ldarg_0 - retorna o argumento com index 0 da Stack
            //  OpCodes.Ldfld - encontra o valor do campo no objeto atual
            //  OpCodes.Ret - retorna o valor de volta a pilha, caso haja valor.
            //
            custNameGetIL.Emit(OpCodes.Ldarg_0);
            custNameGetIL.Emit(OpCodes.Ldfld, fieldBuilder);
            custNameGetIL.Emit(OpCodes.Ret);

            //
            // Assim como criamos um método get, fazemos
            // a mesma coisa para o método set.
            //
            MethodBuilder custNameSetPropMthdBldr = tb.DefineMethod("set_" + propertyName, getSetAttribute, null, new Type[] { typeof(string) });

            //
            // Gera o MSIL que iremos trabalhar
            //
            ILGenerator custNameSetIL = custNameSetPropMthdBldr.GetILGenerator();

            //
            // Para o método set, utilizamos:
            //  OpCodes.Ldarg_0 - retorna o argumento com index 0 da Stack
            //  OpCodes.Ldarg_1 - retorna o argumento com index 1 da Stack
            //  OpCodes.Stfld - altera o valor atual para o novo valor
            //  OpCodes.Ret - retorna o valor de volta a pilha, caso haja valor.
            //
            custNameSetIL.Emit(OpCodes.Ldarg_0);
            custNameSetIL.Emit(OpCodes.Ldarg_1);
            custNameSetIL.Emit(OpCodes.Stfld, fieldBuilder);
            custNameSetIL.Emit(OpCodes.Ret);

            //
            // Feito tudo isso acima, devemos informar ao nosso PropertyBuilder
            // nossos dois métodos (get/set) correspondentes.
            //
            propertyBuilder.SetGetMethod(custNameGetPropMthdBldr);
            propertyBuilder.SetSetMethod(custNameSetPropMthdBldr);

            //
            // Para finalizar criamos a classe.
            // Basta invocarmos o método CreateType do nosso TypeBuilder
            //
            Type t = tb.CreateType();

            //
            // Através do método CreateInstance da class Activator
            // geramos uma instancia de nossa nova classe.
            //
            object instance = Activator.CreateInstance(t);

            //
            // Agora podemos invocar nossa propriedade
            // e setar um valor para ela.
            // Atente para os parâmetros passados para o
            // método InvokeMember, além do nome da propriedade
            // temos também uma flag SetProperty, bem como a instancia
            // que acabamos de criar e por ultimo o valor que queremos.
            //
            t.InvokeMember(propertyName, BindingFlags.SetProperty, null, instance, new object[] { "Luciano Lima" });

            //
            // O código abaixo é para testarmos nossa classe.
            // Nele varremos as propriedades para pegarmos
            // o valor que nela(s) foi(ram) setado(s).
            //
            PropertyInfo[] propertyInfo = t.GetProperties();

            foreach (var info in propertyInfo)
            {
                Console.WriteLine(info.Name + ": " + t.InvokeMember(propertyName, BindingFlags.GetProperty, null, instance, null));
            }

            Console.ReadLine();
        }
    }
}

 

Espero que tenham gostado!!

Enjoy!!