How to create a class at runtime?

2

How to create a runtime class, or runtime , dynamically from a Dictionary<TKey, TValue> , for example, with C #?

Dictionary<string, string> dicionario = new Dictionary<string, string>();
dicionario.Add("nome", "string");
dicionario.Add("idade", "int");
dicionario.Add("telefone", "string");
dicionario.Add("cpf", "string");

The TKey would be the name of the properties to be created in the class, whereas TValue would be the type.

    
asked by anonymous 23.02.2017 / 22:42

1 answer

6

Short answer: using System.Reflection.Emit .

Long answer: Is this answer here , but I will not just stick it for you here. I'll explain the principle.

First you need to define the dynamic type constructor. The answer defines it below (I modified it a bit with a few things):

private static TypeBuilder GetTypeBuilder(string libraryName = "MainModule", string typeSignature = "MyDynamicType")
{
    var an = new AssemblyName(typeSignature); // Aqui você vai definir o nome da classe dinâmica
    AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
    ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(libraryName); // Aqui vai definir em qual Class Library sua classe dinâmica vai estar

    TypeBuilder tb = moduleBuilder.DefineType(typeSignature,
            TypeAttributes.Public |
            TypeAttributes.Class |
            TypeAttributes.AutoClass |
            TypeAttributes.AnsiClass |
            TypeAttributes.BeforeFieldInit |
            TypeAttributes.AutoLayout,
            null);
    return tb;
}

The following is the method that creates properties. It needs TypeBuilder pre-mounted to work:

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

You'll probably have to modify your dictionary to get a Type instead of string .

The following is the method that compiles the dictionary in the dynamic class:

public static Type CompileResultType(Dictionary<string, Type> dicionario) // Repare que já mudei aqui pra você
{
    TypeBuilder tb = GetTypeBuilder(); // Aqui constrói a classe num Assembly "MainModule.dll", classe "MyDynamicType". 
                                       // Parametrize da forma que achar melhor. 
    ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

    foreach (KeyValuePair<string, Type> chaveValor in dicionario)
        CreateProperty(tb, chaveValor.Key, chaveValor.Value);

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

Finally, the static method that invokes all others:

public static object CreateNewObject(Dictionary<string, Type> dicionario)
{
    var myType = CompileResultType(dicionario);
    return Activator.CreateInstance(myType);
}

Usage:

var meuObjetoCriadoDiamicamente = MyTypeBuilder.CreateNewObject(dicionario);
    
23.02.2017 / 23:02