2012-09-14 16 views
7

Jugando con hacer un compilador para mi propio idioma, estoy tratando de generar algún código MSIL utilizando el marco Reflection.Emit. Funciona bien cuando se usa int cuando declaro variables locales. Sin embargo, cuando deseo declarar una variable local de un tipo que aún no he compilado, me meto en problemas ya que el DeclareLocal() toma un Type como argumento. Esa es mi clase sin compilar, por ejemplo A, todavía tiene que ser definido usandoILGenerator.DeclareLocal() toma un tipo de una clase aún no compilada

assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemName, AssemblyBuilderAccess.RunAndSave); 
module = assemblyBuilder.DefineDynamicModule(Filename); 
module.DefineType(name, TypeAttributes.Public | TypeAttributes.Class) 

Así que ¿cómo voy a ser capaz de compilar el programa siguiente

class A { 
    void M() { B b = new B(); } 
} 
class B 
    void M() { A a = new A(); } 
} 
+0

¿Qué ayudaría? –

+1

@phoog: bien podría esperar a que alguien haga su trabajo, entonces ... –

+0

No estoy haciendo C# aquí ... era solo un ejemplo de dependencia circular –

Respuesta

7

La idea principal que necesita aquí es que TypeBuilder deriva de Type. Por lo tanto, incluso si aún no finalizó un tipo (llamando al CreateType()), puede usarlo para declarar una variable local en otro tipo.

Una barrera más que encontré es que GetConstructor() en un TypeBuilder inacabado no funciona (arroja una excepción). Pero si crea el constructor predeterminado explícitamente, puede llamarlo a través del ConstructorBuilder.

static void Main() 
{ 
    var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
     new AssemblyName("foo"), AssemblyBuilderAccess.RunAndSave); 
    var module = assemblyBuilder.DefineDynamicModule("foo.dll"); 
    var aType = module.DefineType(
     "A", TypeAttributes.Public | TypeAttributes.Class); 
    var bType = module.DefineType(
     "B", TypeAttributes.Public | TypeAttributes.Class); 
    var aCtor = aType.DefineDefaultConstructor(MethodAttributes.Public); 
    var bCtor = bType.DefineDefaultConstructor(MethodAttributes.Public); 
    CreateMethodM(aType, bType, bCtor); 
    CreateMethodM(bType, aType, aCtor); 
    aType.CreateType(); 
    bType.CreateType(); 
    assemblyBuilder.Save("foo.dll"); 
} 

static void CreateMethodM(
    TypeBuilder thisType, Type otherType, ConstructorInfo otherCtor) 
{ 
    var method = thisType.DefineMethod(
     "M", MethodAttributes.Private, typeof(void), Type.EmptyTypes); 
    var il = method.GetILGenerator(); 
    var local = il.DeclareLocal(otherType); 
    il.Emit(OpCodes.Newobj, otherCtor); 
    il.Emit(OpCodes.Stloc, local); 
    il.Emit(OpCodes.Ret); 
} 
+1

Argh! ¡Mi solución es tan cercana a la tuya! Estaba cegado por querer llamar "t.DeclaringType" en lugar de simplemente "t" –

+2

'DeclaringType' es algo diferente, representa el tipo en el que se declaró este tipo, por ejemplo, cuando se trata de un tipo anidado. – svick

Cuestiones relacionadas