views:

47

answers:

1

Hey,

I'm generating (using System.Reflection.Emit) two types: call them foo, bar. The catch is, foo instantiates and calls bar, and bar uses foo.

All works fine when I create bar, but when I then start generating foo, I get typeloadexception saying that type foo could not be found. It happens (probably, as the error was vague) when I try to locate the constructor to bar, which as one of its parameters takes foo.

This works when bar is nested type in foo.

So my question is - is it illegal to have two types calling each other like this, or am I doing it wrong?

+1  A: 

Trying to locate the constructor manually may be hard, but you should still have the one you generated earlier? Have you tried passing it that one? I'll try to do an example...

    var assemblyName = new AssemblyName("tmp");
    var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
    var module = assembly.DefineDynamicModule("tmp");
    var foo = module.DefineType("Foo");
    var bar = module.DefineType("Bar");
    var barOnFoo = foo.DefineField("bar", bar, FieldAttributes.Private);
    var fooOnBar = bar.DefineField("foo", foo, FieldAttributes.Private);
    var barCtor = bar.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new Type[] { foo });
    var il = barCtor.GetILGenerator();
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Ldarg_1);
    il.Emit(OpCodes.Stfld, fooOnBar);
    il.Emit(OpCodes.Ret);
    var fooCtor = foo.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, Type.EmptyTypes);
    il = fooCtor.GetILGenerator();
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Newobj, barCtor);
    il.Emit(OpCodes.Stfld, barOnFoo);
    il.Emit(OpCodes.Ret);

    // create the actual types and test object creation
    Type fooType = foo.CreateType(), barType = bar.CreateType();
    object obj = Activator.CreateInstance(fooType);

I could add extra code to check the result, but it is easier just to look at obj in the debugger, and you can see the fields etc.

For more complex cases - don't forget that you don't need to write the body of a method (the IL) to use it... you can write all the signatures first (DefineMethod, DefineConstructor, etc), and then write all the bodies after, allowing fully cyclic code.

Marc Gravell
Turns out, the error lied somewhere else. The method that bar called on foo, was protected, so life was good when bar was nested in foo, when I moved it out, it was not able to call that method. I changed it to public and life's good again.Turns out SRE and 2am don't mix.Thanks anyway.
Krzysztof Koźmic