Create a class dynamically in C#

Asked

Viewed 4,278 times

9

I have a class "Classea" and this has an 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 would like to create classes in Runtime that inherit this class "A" and that they populate the attribute 'Attribute'.

  • Why does the class need to be created dynamically? Is there a reason for this? There’s even a way to do it even though it’s not a class of truth that will be created (Expandoobject). But as far as I know can not use in inheritance. Nor would make sense. I think your problem is another. try to provide more information to indicate better the problem.

  • take a look at this link, maybe it’s what you want. http://msdn.microsoft.com/pt-br/library/ms404245(v=vs.110). aspx

  • There’s even a way to create truth classes with Reflection or the new .Net Compiler Platform But I doubt that’s what you need, these are very complex resources to be used in very non-trivial situations like this problem seems to be. I think you’re just trying to create parameterized objects and not classes.

  • Because I use this base class to include mapping of entities in a context that is housed in a container, and the location that calls the method that "runs" that insertion of entities in context, checks whether the context class that will receive entities, no longer exists in an internal dictionary and does not allow it to occur more than once by the same class type. Soon, the idea arose of creating in Runtime instances of classes that inherit the methods of this base class, so that it is not necessary to 'codate' for each context a new class and use this generic class.

  • 2

    There are two methods of generating Runtime code, TypeBuilder and CodeDOM. TypeBuilder is extremely difficult to use as it works at IL level. I agree with @bigown, I seriously doubt this is necessary to solve the problem.

  • People walk fast on the trigger :D Yes, you can create real classes at runtime. Yes, you can create inheritance in Runtime. Yes, this can be very useful (many Orms work like this, inheriting our entities and adding resources to them). I just think that the AP has already tried something and in this case it could show what it has already achieved and the difficulties. The question already served at least to show that there is something that some did not dream.

  • 2

    Creating class instances is very different from creating classes. Instances inherit nothing, instances do not need. I understood the general context but I still have doubts whether the approach would be correct. It seems to me that you’re stuck with the concepts of OOP as if they solve anything simply. There are other ways to generalize the solution if this is so important. If it is not, there is no point in going to the trouble. See something about this at http://answall.com/a/15432/101

  • 4

    People, how can you close the question because it is not clear? The question is very clear! @bigown Has nothing to do with writing and compiling C#code. Net has a specific API for defining types in Runtime.

  • 1

    If you find the question clear and know how to resolve post your answer using the API. I think this problem does not need any of this but if I needed I would use the most practical way and now recommended by MS.

Show 4 more comments

3 answers

6


The code below defines in Runtime a ClasseB you inherit from your ClasseA, where I added private fields to use in the properties instead of leaving it to the compiler.

The approach I used to ClasseB seven the property Atributo of ClasseA added a new constructor to it (in Classeb), with a more.

Tested and working: https://dotnetfiddle.net/y6M72F

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: documentation of the Assemblybuilder.

The key in this example are the classes TypeBuilder, for the definition of a type in Runtime, and the ILGenerator, to add instructions to the body of 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

It is clear that this code is for educational purposes only and needs to be much more readable if used. It is also possible to generalize this code and abstract it to a nicer API that meets a specific need.

Besides, I imagine you’d wear Attributes do . Net’s 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 project runtime or build time) is in frameworks that do not want to force the user to inherit their classes but need to add resources to the user objects (ORMs, for example).

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);
        }
    }
}

Use:

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.

-1

use the dynamic teste = new ExpandoObject(); is much easier ;)

  • 2

    Could you briefly explain why this option/code is better and what advantages it has? I recommend reading the [tour] to learn the basics of the site.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.