Create a class dynamically in C #

9

I have a class "ClassA" and it has a "Attribute" attribute protected.

public class ClasseA
{
    protected string Atributo { get; set; }
    public int Codigo { get; set; }
    public ClasseA (int codigo)
    {
        Codigo = codigo;
    }
    public string Concatena()
    {
        return this.Atributo + " - " + this.Codigo;
    }

}

I'd like to create runtime classes that inherit from this "A" class and have them populate the 'Attribute' attribute.

    
asked by anonymous 20.11.2014 / 18:16

3 answers

6

The code below sets the ClasseB to runtime that inherits from its ClasseA , in which I added private fields to use in the properties instead of leaving it to the compiler.

The approach I used to have ClasseB seven the Atributo property of ClasseA was adding a new constructor, with one parameter to it, in ClassB.

Tested and working: link

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

public class Program
{
    public static void Main()
    {
        CriaClasseHerdandoOutra();
    }
    public static void CriaClasseHerdandoOutra() {

        AssemblyName aName = new AssemblyName("DynamicAssemblyExample");
        AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.RunAndSave);
        ModuleBuilder mb = ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");

        // define tipo "ClasseB" herdando de "ClasseA"
        Type typeClasseA = Type.GetType("ClasseA");
        TypeBuilder tb = mb.DefineType("ClasseB", TypeAttributes.Public, typeClasseA);

        // obtém informação de atributos da "ClasseA" que serão setados 
        // no novo construtor da classe filha "ClasseB"
        FieldInfo codigo = typeClasseA.GetField("_codigo", BindingFlags.NonPublic | BindingFlags.Instance);
        FieldInfo atributo = typeClasseA.GetField("_atributo", BindingFlags.NonPublic | BindingFlags.Instance);

        // cria no tipo "ClasseB" o construtor exigido por sua classe base
        Type[] parameterTypes = {typeof(int)};
        ConstructorBuilder ctor1 = tb.DefineConstructor(
            MethodAttributes.Public, 
            CallingConventions.Standard, 
            parameterTypes);

        // adiciona no construtor instruções para setar 
        // o atributo "Codigo" da classe base "ClasseA"
        ILGenerator ctor1IL = ctor1.GetILGenerator();
        ctor1IL.Emit(OpCodes.Ldarg_0);
        ctor1IL.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));
        ctor1IL.Emit(OpCodes.Ldarg_0);
        ctor1IL.Emit(OpCodes.Ldarg_1);
        ctor1IL.Emit(OpCodes.Stfld, codigo);
        ctor1IL.Emit(OpCodes.Ret);

        // cria no tipo "ClasseB" um novo construtor,
        // que recebe dois parâmetros
        Type[] parameterTypes2 = { typeof(int), typeof(string)};
        ConstructorBuilder ctor2 = tb.DefineConstructor(
            MethodAttributes.Public, 
            CallingConventions.Standard, 
            parameterTypes2);

        // adiciona no novo construtor instruções para setar 
        // os atributos "Codigo" e "Atributo" da classe base "ClasseA"
        ILGenerator ctor2IL = ctor2.GetILGenerator();
        ctor2IL.Emit(OpCodes.Ldarg_0);
        ctor2IL.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));
        ctor2IL.Emit(OpCodes.Ldarg_0);
        ctor2IL.Emit(OpCodes.Ldarg_1);
        ctor2IL.Emit(OpCodes.Stfld, codigo);
        ctor2IL.Emit(OpCodes.Ldarg_0);
        ctor2IL.Emit(OpCodes.Ldarg_2);
        ctor2IL.Emit(OpCodes.Stfld, atributo);
        ctor2IL.Emit(OpCodes.Ret);

        // cria o tipo que foi definido para "ClasseB"
        Type ClasseBType = tb.CreateType();

        // cria uma instância da "ClasseB", que acaba de ser definida
        // em runtime (herdando de "ClasseA"), invocando seu novo construtor
        // de dois parâmetros.
        ClasseA objClasseB = (ClasseA)System.Activator.CreateInstance(ClasseBType, 1, "Rá!");

        // invoca o método "Concatena" da classe base "ClasseA",
        // o qual teve seu resultado afetado pela classe filha "ClasseB"
        Console.WriteLine(objClasseB.Concatena());
    }        
}

public class ClasseA
{
    protected string _atributo;
    protected int _codigo;

    protected string Atributo 
    { 
        get {return _atributo;}
        set {_atributo = value; }
    }
    public int Codigo 
    { 
        get {return _codigo;}
        set {_codigo = value; }
    }
    public ClasseA (int codigo)
    {
        Codigo = codigo;
    }
    public string Concatena()
    {
        return this.Atributo + " - " + this.Codigo;
    }
}

Reference: AssemblyBuilder documentation .

The key in this example is the TypeBuilder classes, for definition of a type in runtime, and ILGenerator , to add instructions to the body of the defined methods.

There are other ways to achieve similar results. Namely - CodeDOM and Roslyn , released in April this year ( an example ).

Considerations about my code

Of course, this code is for teaching purposes only and needs to be much more readable if used. You can also generalize this code and abstract it into a cooler API that meets a specific need.

Also, I suspect you'd use Attributes . Net to identify which properties to change instead of doing so based on the name.

A typical example of using this type of engineering (creating class inheritance at runtime or project compilation) is in frameworks that do not want to force the user to inherit their classes but need to add resources to the user objects (< in> ORM s, for example).

    
20.11.2014 / 21:48
5

First implement a special class to build other classes. The following code does this:

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

namespace TypeBuilderNamespace
{
    public class ObjectBuilder
    {
        public object MyObject { get; set; }
        // Key seria o nome da propriedade, e Value o tipo dela.
        public Dictionary<string, string> Fields { get; set; }

        public void CreateNewObject()
        {
            var myType = CompileResultType();
            MyObject = Activator.CreateInstance(myType);
        }

        public Type CompileResultType()
        {
            TypeBuilder tb = GetTypeBuilder();
            ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

            foreach (var field in Fields)
                CreateProperty(tb, field.Key, field.Value);

            Type objectType = tb.CreateType();
            return objectType;
        }

        private TypeBuilder GetTypeBuilder()
        {
            var typeSignature = "DerivadaDeClasseA";
            var an = new AssemblyName(typeSignature);
            AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
            TypeBuilder tb = moduleBuilder.DefineType(typeSignature
                                , TypeAttributes.Public |
                                TypeAttributes.Class |
                                TypeAttributes.AutoClass |
                                TypeAttributes.AnsiClass |
                                TypeAttributes.BeforeFieldInit |
                                TypeAttributes.AutoLayout
                                , null);
            return tb;
        }

        private void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType)
        {
            FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);

            PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
            MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
            ILGenerator getIl = getPropMthdBldr.GetILGenerator();

            getIl.Emit(OpCodes.Ldarg_0);
            getIl.Emit(OpCodes.Ldfld, fieldBuilder);
            getIl.Emit(OpCodes.Ret);

            MethodBuilder setPropMthdBldr =
                tb.DefineMethod("set_" + propertyName,
                  MethodAttributes.Public |
                  MethodAttributes.SpecialName |
                  MethodAttributes.HideBySig,
                  null, new[] { propertyType });

            ILGenerator setIl = setPropMthdBldr.GetILGenerator();
            Label modifyProperty = setIl.DefineLabel();
            Label exitSet = setIl.DefineLabel();

            setIl.MarkLabel(modifyProperty);
            setIl.Emit(OpCodes.Ldarg_0);
            setIl.Emit(OpCodes.Ldarg_1);
            setIl.Emit(OpCodes.Stfld, fieldBuilder);

            setIl.Emit(OpCodes.Nop);
            setIl.MarkLabel(exitSet);
            setIl.Emit(OpCodes.Ret);

            propertyBuilder.SetGetMethod(getPropMthdBldr);
            propertyBuilder.SetSetMethod(setPropMthdBldr);
        }
    }
}

Usage:

var dicionario = new Dictionary<string, object>();
dicionario.Add("Atributo", typeof(String));

var objectBuilder = new ObjectBuilder {
    Fields = dicionario 
};

objectBuilder.CreateNewObject();
var objetoCriado = objectBuilder.MyObject;

PS: I have not tested this code, and some adjustments may be necessary, but it is a starting point.

    
20.11.2014 / 20:53
-1

Using dynamic teste = new ExpandoObject(); is much easier;)

    
18.10.2016 / 15:17