Question

I am trying to use Refle.emit to generate the following class:

public class Parent {
    public class Child { }
    public Child MyChild { get; set; }
}

So this is what did:

static void Main(string[] args) {
    AssemblyName newAssembly = new AssemblyName("myAssembly");
    AppDomain appDomain = System.Threading.Thread.GetDomain();
    AssemblyBuilder assemblyBuilder =
    appDomain.DefineDynamicAssembly(newAssembly, AssemblyBuilderAccess.RunAndSave);
    ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(newAssembly.Name);

    TypeBuilder parentBuilder = moduleBuilder.DefineType("Parent");
    TypeBuilder childBuilder = parentBuilder.DefineNestedType("Child");
    parentBuilder.DefineProperty("MyProperty", PropertyAttributes.None, childBuilder.CreateType(), null);
    parentBuilder.CreateType();
}

I get an exception: "Could not load type 'Parent' from assembly 'myAssembly'" at parentBuilder.DefineProperty(...,

I didnt find any way to create the parent class without creating the child class in a different assembly. Any suggestions?

Was it helpful?

Solution

You can create the parent class without creating the child class in a different assembly and in fact your code is close to be correct. In you code you have one mistake in line:

parentBuilder.DefineProperty("MyProperty", PropertyAttributes.None, childBuilder.CreateType(), null);

Here you are trying to CreateType of nested type, which is unnecessary at this moment. You can provide just a TypeBuilder for it:

parentBuilder.DefineProperty("MyProperty", PropertyAttributes.None, childBuilder, null);

But remember, that defining a property in such way is not enough. You must provide a implementation for its setter and getter (and probably a backing field). Here you have a working example for generation of class:

public class Parent
{
    public class Child
    {
    }
    private Parent.Child myChild;
    public Parent.Child MyChild
    {
        get
        {
            return this.myChild;
        }
        set
        {
            this.myChild = value;
        }
    }
}

Code:

TypeBuilder parentBuilder = moduleBuilder.DefineType("Parent", TypeAttributes.Public);
TypeBuilder childBuilder = parentBuilder.DefineNestedType("Child", TypeAttributes.NestedPublic);
PropertyBuilder propertyBuilder = parentBuilder.DefineProperty("MyChild", PropertyAttributes.None, childBuilder, null);

// Define field
FieldBuilder fieldBuilder = parentBuilder.DefineField("myChild", childBuilder, FieldAttributes.Private);
// Define "getter" for MyChild property
MethodBuilder getterBuilder = parentBuilder.DefineMethod("get_MyChild",
                                    MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
                                    childBuilder,
                                    Type.EmptyTypes);
ILGenerator getterIL = getterBuilder.GetILGenerator();
getterIL.Emit(OpCodes.Ldarg_0);
getterIL.Emit(OpCodes.Ldfld, fieldBuilder);
getterIL.Emit(OpCodes.Ret);

// Define "setter" for MyChild property
MethodBuilder setterBuilder = parentBuilder.DefineMethod("set_MyChild", 
                                    MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
                                    null,
                                    new Type[] { childBuilder });
ILGenerator setterIL = setterBuilder.GetILGenerator();
setterIL.Emit(OpCodes.Ldarg_0);
setterIL.Emit(OpCodes.Ldarg_1);
setterIL.Emit(OpCodes.Stfld, fieldBuilder);
setterIL.Emit(OpCodes.Ret);

propertyBuilder.SetGetMethod(getterBuilder);
propertyBuilder.SetSetMethod(setterBuilder);

OTHER TIPS

In my sample below, I removed the creation of the Child type to focus on debugging the creation of the Parent type:

    public static Type BuildType() {
        AssemblyName newAssembly = new AssemblyName("myAssembly");
        //AppDomain appDomain = System.Threading.Thread.GetDomain();

        AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain
            .DefineDynamicAssembly(
            newAssembly, 
            AssemblyBuilderAccess.RunAndSave);

        ModuleBuilder moduleBuilder = assemblyBuilder
            .DefineDynamicModule(newAssembly.Name, newAssembly.Name + ".dll");

        TypeBuilder parentBuilder = moduleBuilder.DefineType(
            "Parent", TypeAttributes.Public);

        var ctor0 = parentBuilder.DefineConstructor(
            MethodAttributes.Public,
            CallingConventions.Standard,
            Type.EmptyTypes);

        ILGenerator ctor0IL = ctor0.GetILGenerator();
        // For a constructor, argument zero is a reference to the new 
        // instance. Push it on the stack before pushing the default 
        // value on the stack, then call constructor ctor1.
        ctor0IL.Emit(OpCodes.Ldarg_0);
        ctor0IL.Emit(OpCodes.Ldc_I4_S, 42);
        ctor0IL.Emit(OpCodes.Call, ctor0);
        ctor0IL.Emit(OpCodes.Ret);

        //TypeBuilder childBuilder = parentBuilder.DefineNestedType("Child");
        //var chType = childBuilder.CreateType();

        parentBuilder.DefineProperty(
            "MyProperty",
            PropertyAttributes.HasDefault,
            typeof(string),
            //childBuilder.CreateType(),
            null);
        var type = parentBuilder.CreateType();

        assemblyBuilder.Save(newAssembly.Name + ".dll");


        return type;
    }

I added a couple of things as I was going along (incl. the default ctor). I got this from MSDN Assembly Builder.

Anyway, this compiles and runs. The problem (I strongly suspect) is that you are not completing the Child type in your code. Complete the Child type and it should load fine. Parent is not loading because of a problem loading Child.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top