2012-11-07 10 views
8

Czy w języku C# można utworzyć typ w środowisku wykonawczym, który dziedziczy po klasie ogólnej, w której parametrem szablonu dla klasy bazowej jest konstruowana bieżąca klasa? Będzie to skompilować grzywny:Utwórz typ "MyClass: OtherClass <MyClass> {}" w czasie wykonywania?

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

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

ale nie jestem pewien, jak stworzyć MyClass użyciu 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(); 

Czy to możliwe?

Edit - Na podstawie odpowiedzi tak daleko, próbowałem to:

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; 
    } 
} 

ale uzyskać ten wyjątek:

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

Ale ... Dlaczego chcesz to zrobić? – LightStriker

+0

@LightStriker: Ponieważ 99% typów 'MyClass' jest zdefiniowanych w kodzie, ale kilka musi być utworzonych po wdrożeniu (modele NHibernate) i' OtherClass' ogranicza, że ​​typ ogólny to taki, który dziedziczy po 'OtherClass'. – wes

+0

Edytowałem swoją odpowiedź - to zadziała teraz, bez wyjątku GenericArgument. – gracchus

Odpowiedz

6

Edycja: Oto moja ostateczna odpowiedź robocza:

 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); 

Sztuką jest, aby zadzwonić SetParent z sparametryzowane typu rodzajowego parametr jest typebuilder typu buduje się.


Użyj metody TypeBuilder.SetParent(Type parent).

Bądź ostrożny podczas korzystania z niego, rzuca wyjątek jest odroczone do CreateType rozmowy:

Jeśli rodzic jest null, obiekt jest używany jako typ bazowy.

W systemach .NET Framework w wersjach 1.0 i 1.1, wyjątek nie zostanie zgłoszony, jeśli element macierzysty jest typem interfejsu, ale wyjątek TypeLoadException jest generowany po wywołaniu metody CreateType.

Metoda SetParent nie sprawdza w przypadku większości niepoprawnych typów rodzica. Na przykład, nie odrzuca typu nadrzędnego, który nie ma konstruktora domyślnego, gdy bieżący typ ma domyślny konstruktor, nie odrzuca typów zapieczętowanych i nie odrzuca typu Delegata. We wszystkich tych przypadkach wyjątki są generowane przez metodę CreateType.

zbudować ogólny typ OtherClass<T>, użyj metody MakeGenericType:

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

Edytowałem mój wpis, aby uwzględnić to.Niestety dostałem wyjątek, że 'MyClass' narusza ograniczenie typu' T'. – wes

+1

To prawda! Dzięki! – wes

1

Tak. To jest możliwe. Zobacz metody nazw Reflection.Emit i TypeBuilder.

+0

On już używa Reflection.Emit. Nie zrozumiałeś tego pytania. – leppie

Powiązane problemy