2012-04-10 8 views
10

Ich muss dynamisch eine Klasse erstellen. Die meisten Dinge funktionieren gut, aber ich bin beim Generieren des Konstruktors stecken geblieben.Dynamisch Typ und Aufruf Konstruktor der Basisklasse erstellen

AssemblyBuilder _assemblyBuilder = 
     AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("MyBuilder"),              AssemblyBuilderAccess.Run); 

ModuleBuilder _moduleBuilder = _assemblyBuilder.DefineDynamicModule("MyModule"); 

public static object GetInstance<TSource, TEventArgs>(this TSource source, string eventName) 
    where TSource : class 
{ 
    var typeName = "MyTypeName"; 
    var typeBuilder = _moduleBuilder.DefineType(typeName, TypeAttributes.Class | TypeAttributes.Public); 

    // create type like class MyClass : GenericType<MyClass, TSource, TEventArgs> 
    var baseNotGenericType = typeof(GenericType<,,>); 
    var baseType = baseNotGenericType.MakeGenericType(typeBuilder, typeof(TSource), typeof(TEventArgs)); 
    typeBuilder.SetParent(baseType); 


    // the base class contains one constructor with string as param 
    var baseCtor = baseNotGenericType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(string) }, null); 

    var ctor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard | CallingConventions.HasThis, new Type[0]); 
    var ilGenerator = ctor.GetILGenerator(); 

    // i want to call the constructor of the baseclass with eventName as param 
    ilGenerator.Emit(OpCodes.Ldarg_0); // push "this" 
    ilGenerator.Emit(OpCodes.Ldstr, eventName); // push the param 
    ilGenerator.Emit(OpCodes.Call, baseCtor); 
    ilGenerator.Emit(OpCodes.Ret); 

    var type = typeBuilder.CreateType(); 

    // return ... 
} 

Auf Aufruf des Konstruktors Ich erhalte eine BadImageFormatException. Was mache ich falsch?

Wie gewünscht:

Die Basisklasse etwa wie folgt aussieht:

public abstract class GenericType<GT, TEventSource, TEventArgs> : BaseClass 
    where GT: GenericType<GT, TEventSource, TEventArgs>, new() 
    where TEventArgs : EventArgs 
    where TEventSource : class 
{ 
    protected GenericType(string eventName) 
    { 
     _eventName = eventName; 
    } 
    // ... 
} 

Was Ich mag würde als Folge in der Laufzeit haben:

public class MyType : BaseClass<MyType, ConcreteSourceType, ConcreteEventArgsType> 
{ 
    protected MyType() : base("SomeName") 
    { 

    } 
} 
+0

Werfen sie einen Blick http://stackoverflow.com/questions/893423/how-do-create-a-dynamic-class-in-c-sharp-4 –

+0

Nicht wirklich hilfreich oder habe ich etwas vermisst? Ich möchte von einer Basisklasse erben und seinen Basiskonstruktor aufrufen, der ein Argument hat. – SACO

+0

Versuchen Sie diesen Link auch http://blogs.msdn.com/b/cburrows/archive/2009/04/22/dynamic-base-classes-in-c-4.aspx –

Antwort

10

Ich denke, das Problem ist, dass Sie versuchen, den Konstruktor des offenen generischen Typs GenericType<GT, TEventSource, TEventArgs> aufzurufen, aber Sie müssen den Konstruktor des geschlossenen Typs BaseClass<MyType, ConcreteSourceType, ConcreteEventArgsType> aufrufen. Die Lösung für das scheint einfach:

var baseCtor = baseType.GetConstructor(
    BindingFlags.NonPublic | BindingFlags.Instance, null, 
    new[] { typeof(string) }, null); 

Das Problem ist, dass dies nicht und NotSupportedException wirft funktioniert. So scheint es, den Konstruktor eines generischen Typs zu bekommen, wo einer der Parameter TypeBuilder ist Nuss unterstützt wird.

Aus diesem Grund denke ich, was Sie wollen, ist nicht mit Reflection.Emit möglich, es sei denn, es gibt einige Hack um dies zu umgehen.

EDIT: A-HA! Ich musste tief in Reflection eintauchen. Emit in Reflector (obwohl das Betrachten der richtigen Stelle in der Dokumentation auch funktioniert hätte), aber ich fand es: es gibt eine spezielle Methode genau dafür: the static TypeBuilder.GetConstructor(). So sollte diese Arbeit:

var baseNonGenericCtor = baseNotGenericType.GetConstructor(
    BindingFlags.NonPublic | BindingFlags.Instance, null, 
    new[] { typeof(string) }, null); 
var baseCtor = TypeBuilder.GetConstructor(baseType, baseNonGenericCtor); 
+0

So freue ich mich auf einen Hack :) – SACO

+0

Eigentlich ist es möglich und Es ist überhaupt kein Hack, siehe Bearbeiten. – svick

+0

BTW, Fragen wie diese, die mich etwas Neues lernen lassen, sind die besten. – svick

3

Der einfachste Weg, dies zu tun wäre, um Ihre abstrakten und abgeleiteten Klassen in eine einfache Montage zu kompilieren, dann öffnen sie in Reflector mit dem „Reflection.Emit“ Sprache verfügbar als Addin von:

http://reflectoraddins.codeplex.com/

Reflector: Reflection.Emit language

Ja, das ist so cool, wie es sich anhört :)

+0

Sind Sie sicher, dass es in diesem speziellen Fall funktionieren würde? – svick

+0

Wenn Ihre gewünschte Klassendefinition gültig ist C# und kompiliert wird, dann ja. – x0n

+0

Nun, der Code, den es in diesem Fall erzeugt, macht nicht viel Sinn und würde dem OP nicht helfen. Das liegt daran, dass 'typeof (MyType)' verwendet wird, was beim Erstellen dieses Typs nicht sinnvoll ist. Und stattdessen würde 'typeBuilder' zu einer' NotSupportedException' führen. – svick

Verwandte Themen