2012-09-14 14 views
7

Bawiąc się przy tworzeniu kompilatora dla mojego własnego języka, próbuję wygenerować kod MSIL za pomocą struktury Reflection.Emit. Działa dobrze podczas używania int, gdy deklaruję zmienne lokalne. Jednakże, gdy chcę zadeklarować zmienną lokalną typu, którego jeszcze nie skompilowałem, wpadam w kłopoty, ponieważ DeclareLocal() przyjmuje argument jako Type. To jest mój Nieskompilowany klasy, powiedzmy A, nadal musi być określona przy użyciuILGenerator.DeclareLocal() przyjmuje typ klasy, która nie została jeszcze skompilowana

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

więc jak kiedykolwiek będę w stanie skompilować następujący program

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

Co by to pomogło? –

+1

@phoog: mógłby równie dobrze poczekać, aż ktoś wykona swoją pracę, a następnie .. –

+0

Nie robię faktycznie C# tutaj .. był to tylko przykład zależności cyklicznej –

Odpowiedz

7

Podstawowym wgląd trzeba tutaj jest to, że TypeBuilder pochodzi od Type. Tak więc, nawet jeśli nie sfinalizowałeś jeszcze typu (przez wywołanie CreateType()), możesz użyć go do zadeklarowania zmiennej lokalnej w innym typie.

Kolejną barierą, na którą natrafiłem, jest to, że GetConstructor() na niedokończonym TypeBuilder nie działa (zgłasza wyjątek). Ale jeśli jawnie utworzysz domyślny konstruktor, możesz go wywołać przez 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! Moje rozwiązanie jest tak blisko Ciebie! Byłem zaślepiony, gdy chciałbym nazwać "t.DeclaringType" zamiast po prostu "t" –

+2

'DeclaringType' jest czymś innym, reprezentuje typ, w którym zadeklarowano ten typ, na przykład, gdy jest to typ zagnieżdżony. – svick