2012-11-07 7 views
8

E 'possibile in C# creare un tipo in runtime che erediti da una classe generica in cui il parametro template per la classe base è la classe corrente in costruzione? Questo compilerà bene:Crea tipo "MyClass: OtherClass <MyClass> {}" in fase di esecuzione?

// I have this class: 
public class OtherClass<T> 
    where T : OtherClass<T> 
{ } 

// I want to create this at runtime: 
public class MyClass : OtherClass<MyClass> 
{ } 

ma non sono sicuro di come creare il MyClass utilizzando System.Reflection.Emit.ModuleBuilder.TypeBuilder:

AssemblyName asn = new AssemblyName("test.dll"); 
AssemblyBuilder asb = AppDomain.CurrentDomain.DefineDynamicAssembly(
    asn, AssemblyBuilderAccess.RunAndSave, @"D:\test_assemblies"); 

ModuleBuilder = modb = asb.DefineDynamicModule("test", "test.dll"); 

TypeBuilder tb = modb.DefineType(
    "test", 
    TypeAttributes.Public | TypeAttributes.Class, 
    typeof(MyClass)); // How to specify inheritance? 

// populate properties, fields, methods, etc., emit... 

tb.CreateType(); 

è possibile?

Edit - Sulla base di risposte finora, ho provato questo:

public class OtherClass<T> 
    where T : OtherClass<T> 
{ } 

public static void CreateSimple() 
{ 
    AssemblyName asn = new AssemblyName("test"); 
    AssemblyBuilder asb = AppDomain.CurrentDomain.DefineDynamicAssembly(
     asn, AssemblyBuilderAccess.RunAndSave, @"D:\test_asms"); 
    ModuleBuilder modb = asb.DefineDynamicModule("test", "test.dll"); 

    try 
    { 
     TypeBuilder tb = modb.DefineType(
      "MyClass", 
      TypeAttributes.Public | TypeAttributes.Class); 

     Type my = tb.CreateType(); 
     tb.SetParent(typeof(OtherClass<>).MakeGenericType(my)); 

     my = tb.CreateType(); 
    } 
    catch (Exception e) 
    { 
     throw; 
    } 
} 

ma ottenere questa eccezione:

GenericArguments[0], 'MyClass', on 'MyProject.Factory+OtherClass`1[T]' 
violates the constraint of type 'T'. 
+2

Ma ... Perché si dovrebbe fare questo? – LightStriker

+0

@LightStriker: poiché il 99% dei tipi 'MyClass' sono definiti nel codice ma alcuni devono essere creati dopo la distribuzione (modelli NHibernate) e' OtherClass' limitano che il tipo generico è uno che eredita da 'OtherClass'. – wes

+0

Ho modificato la mia risposta: funzionerà ora, senza eccezione GenericArgument. – gracchus

risposta

6

Edit: Ecco la mia risposta definitiva di lavoro:

 AssemblyName asn = new AssemblyName("test.dll"); 
     AssemblyBuilder asb = AppDomain.CurrentDomain.DefineDynamicAssembly(
      asn, AssemblyBuilderAccess.RunAndSave, @"D:\test_assemblies"); 

     ModuleBuilder modb = asb.DefineDynamicModule("test", "test.dll"); 

     TypeBuilder tb = modb.DefineType(
      "test", 
      TypeAttributes.Public | TypeAttributes.Class); 
     // Typebuilder is a sub class of Type 
     tb.SetParent(typeof(OtherClass<>).MakeGenericType(tb)); 
     var t2 = tb.CreateType(); 
     var i = Activator.CreateInstance(t2); 

Il trucco è quello di chiamare SetParent con un tipo generico parametrizzato, il parametro è il TypeBuilder del tipo essere in sé costruito.


Utilizzare il metodo TypeBuilder.SetParent(Type parent).

Fare attenzione quando lo si utilizza, ad eccezione lancio è differito al CreateType chiamata:

Se il padre è nullo, oggetto viene utilizzato come tipo di base.

Nelle versioni .NET Framework 1.0 e 1.1, non viene generata alcuna eccezione se parent è un tipo di interfaccia, ma viene generata una TypeLoadException quando viene chiamato il metodo CreateType.

Il metodo SetParent non verifica la maggior parte dei tipi di padre non validi. Ad esempio, non rifiuta un tipo genitore che non ha un costruttore predefinito quando il tipo corrente ha un costruttore predefinito, non rifiuta i tipi sealed e non rifiuta il tipo Delegate. In tutti questi casi, le eccezioni vengono generate dal metodo CreateType.

per costruire il vostro tipo generico OtherClass<T>, utilizzare il metodo MakeGenericType:

var genericType = typeof(OtherClass<>).MakeGenericType(typeof(MyClass)); 
+0

Ho modificato il mio post per includerlo.Purtroppo ho avuto un'eccezione sul fatto che 'MyClass' ha violato il vincolo del tipo' T'. – wes

+1

Questo lo fa! Grazie! – wes

1

Sì. È possibile. Vedi lo spazio dei nomi Reflection.Emit e il metodo di TypeBuilder.

+0

Sta già utilizzando Reflection.Emit. Non hai capito la domanda. – leppie

Problemi correlati