Jaycee
Jaycee

Reputation: 3118

Adding a property of a dynamic type to a dynamic type

I am trying to create dynamic types similar to this:

public class DataObjects
{
  public DataObjectsLesser1 SomeRandom0 { get; }
  public DataObjectsLesser2 SomeRandom1 { get; }
}

public class DataObjectsLesser1 
{
  public int Prop0 { get; }
}

public class DataObjectsLesser2
{
  public int Prop0 { get; }
}

The following complete code example is a cut down version of the real program, which uses a 3rd party api to create a tree structure where you can navigate the nested types.

In the real program I can easily create the top level type DataObjects and also empty versions of DataObjectsLesser1/2. The problem is when I want to create the properties of DataObjectsLesser1/2 - and IF the bug was to be replicated - then the value "Prop" + num in the most inner loop is ignored and the value "SomeRandom" + i * 10 is used for the property name instead. I can't see any logical reason for this as the types are set as expected (int in the example).

I also do not know how to verify what is going on in the example. I get a dynamic type in the instance var with the properties I generated, but each property does not appear typed - just null properties with no way of telling what type has been generated. I need to navigate the object graph for testing.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;

namespace TyeTes
{
    internal class Program
    {
        private static void CreateProperty(TypeBuilder typeBuilder, Type propertyType, string propertyName)
        {
            FieldBuilder fieldBuilder = typeBuilder.DefineField(propertyName + "X", propertyType, FieldAttributes.Private);
            PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
            MethodBuilder propertyGetter = typeBuilder.DefineMethod("get_" + propertyName, MethodAttributes.Public |
                                                MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
            ILGenerator randomPropertyGetterIL = propertyGetter.GetILGenerator();
            randomPropertyGetterIL.Emit(OpCodes.Ldarg_0);
            randomPropertyGetterIL.Emit(OpCodes.Ldfld, fieldBuilder);
            randomPropertyGetterIL.Emit(OpCodes.Ret);
            propertyBuilder.SetGetMethod(propertyGetter);
        }

        private static void Main(string[] args)
        {
            var assembly = new AssemblyName("FieldTypes");
            AssemblyBuilder assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assembly, AssemblyBuilderAccess.Run);
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assembly.Name);
            TypeBuilder typeBuilder = DefineType(moduleBuilder, "DataObjects");

            foreach (var i in Enumerable.Range(0, 3))
            {
                TypeBuilder subTypeBuilder = DefineType(moduleBuilder, "DataObjectsLesser" + i);
                foreach (var num in Enumerable.Range(0, 2))
                {
                    CreateProperty(subTypeBuilder, typeof(int), "Prop" + num);
                }
                CreateProperty(typeBuilder, subTypeBuilder.CreateType(), "SomeRandom" + i * 10);
            }
            Type randomType = typeBuilder.CreateType();
            var instance = Activator.CreateInstance(randomType);
        }

        public static TypeBuilder DefineType(ModuleBuilder modBuilder, string typeName)
        {
            TypeBuilder typeBuilder = modBuilder.DefineType(typeName,
                TypeAttributes.Public |
                TypeAttributes.Class |
                TypeAttributes.AutoClass |
                TypeAttributes.AnsiClass |
                TypeAttributes.BeforeFieldInit |
                TypeAttributes.AutoLayout,
                typeof(object));
            return typeBuilder;
        }
    }
}

Upvotes: 0

Views: 553

Answers (1)

Konrad Kokosa
Konrad Kokosa

Reputation: 16878

Regarding your second question:

I need to navigate the object graph for testing.

The easiest way that comes to my mind is to save dynamic assembly to disk and then look at it in ILSpy or other decompiler. To do that specify AssemblyBuilderAccess.RunAndSave option to DefineDynamicAssembly method and provide a filename in DefineDynamicModule method:

AssemblyBuilder assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assembly, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assembly.Name, 
                                                                 "temp.dll");

add then at the end:

assemblyBuilder.Save("temp.dll");

Upvotes: 2

Related Questions