2016-12-28 2 views
3

Der Code, den ich dynamisch aufbauen wollen, ist wie folgt:Reflection Emit: Wie Konstruktor bauen für diese

public class Sample 
{ 
    public Sample() 
    { 
     Items = new ObservableTestCollection<Sample>(this); 
    } 
    public Sample(IEnumerable<Sample> source) 
    { 
     Items = new ObservableTestCollection<Sample>(this, source); 
    } 
    public ObservableTestCollection<Sample> Items; 
} 

Die Quelle des ObservableTestCollection ist wie folgt:

public class ObservableTestCollection<T> : ObservableCollection<T> 
{ 
    public T Parent;  
    public ObservableTestCollection(T parent) 
    { 
     Parent = parent; 
    } 
    public ObservableTestCollection(T parent, IEnumerable<T> source) : base(source) 
    { 
     Parent = parent; 
    } 
} 

Der Code ich schreibe, ist :

const string assemblyName = "SampleAssembly"; 
const string fieldName = "Items"; 
const string typeName = "Sample"; 
const string assemblyFileName = assemblyName + ".dll"; 

AppDomain domain = AppDomain.CurrentDomain; 
AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.RunAndSave); 
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName, assemblyFileName); 

TypeBuilder typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Class | TypeAttributes.Public); 

Type[] ctorParameters = new Type[] { typeBuilder }; 
Type typeOfCTS = typeof(ObservableTestCollection<>); 
Type genericTypeOTS = typeOfCTS.MakeGenericType(typeBuilder); 


FieldBuilder fieldBuilder = typeBuilder.DefineField(fieldName, genericTypeOTS, FieldAttributes.Public); 

     //first constructor 
ConstructorBuilder ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, Type.EmptyTypes); 
ILGenerator generator = ctorBuilder.GetILGenerator(); 
     generator.Emit(OpCodes.Ldarg_0); //load this 
     generator.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes)); //call object constructor 

var ci = typeOfCTS.GetConstructors()[0]; 
generator.Emit(OpCodes.Newobj, ci);    
generator.Emit(OpCodes.Stfld, fieldBuilder); // store into Items 
generator.Emit(OpCodes.Ret); //return 

//second constructor 
var typeOfIE = typeof(IEnumerable<>); 
var genericTypeIE = typeOfIE.MakeGenericType(typeBuilder);   
ctorParameters = new Type[] {genericTypeIE }; 
ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, ctorParameters); 

ctorParameters = new Type[] { typeBuilder, genericTypeIE }; 
generator = ctorBuilder.GetILGenerator(); 
generator.Emit(OpCodes.Ldarg_0); //load this 

ci = typeOfCTS.GetConstructors()[1]; 
generator.Emit(OpCodes.Newobj, ci); 
generator.Emit(OpCodes.Stfld, fieldBuilder); // store into Items 
generator.Emit(OpCodes.Ret); //return 
Type type = typeBuilder.CreateType(); 
var obj = Activator.CreateInstance(type); 
assemblyBuilder.Save(assemblyFileName); 

Ich kann keine Instanz von Sample erstellen.

Kann mir jemand helfen, dieses Problem zu beheben?

Ihre Hilfe würde sehr geschätzt werden.

+0

Was meinen Sie mit "Ich kann keine Instanz von Sample erstellen"? Haben Sie während der Ausführung eine Fehlermeldung? Ein Kompilierungsfehler oder eine Warnung? –

+0

Bitte bearbeiten Sie Ihre Frage und fügen Sie die Nachricht –

+0

Die Nachricht, die ich bekam, ist unten zitiert: {"Ein Versuch wurde ein Programm mit einem falschen Format geladen. (Ausnahme von HRESULT: 0x8007000B)"} –

Antwort

2

Grund für diesen Fehler ist der Aufruf des Konstruktors für den offenen generischen Typ. Sie müssen den Konstruktor für den geschlossenen generischen Typ mit TypeBuilder als generischen Parameter abrufen. Es gibt einige Probleme mit der ConstructorInfo, die here erklärt.

So Lösung ist TypeBuilder.GetConstructor(Type, ConstructorInfo) statische Methode aufrufen, mit folgenden Parametern (wie @TonyTHONG bereits erwähnt):

  • Type hatte generischen Typ auf TypeBuilder geschlossen sein, in Ihrem Fall typeof(ObservableTestCollection<>).MakeGenericType(typeBuilder);
  • ConstructorInfo, dass Sie von offenen generischen Typ, in Ihrem Fall typeof(ObservableTestCollection<>) bekommen können.

Sie können Codebeispiel für Ihr Problem unten:

 const string assemblyName = "SampleAssembly"; 
     const string fieldName = "Items"; 
     const string typeName = "Sample"; 
     const string assemblyFileName = assemblyName + ".dll"; 

     var domain = AppDomain.CurrentDomain; 
     var assemblyBuilder = domain.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.RunAndSave); 

     var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName, assemblyFileName); 
     var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Class | TypeAttributes.Public); 

     var typeOfCts = typeof(ObservableTestCollection<>); 
     var genericTypeOfCts = typeOfCts.MakeGenericType(typeBuilder); 

     var fieldBuilder = typeBuilder.DefineField(fieldName, genericTypeOfCts, FieldAttributes.Public); 

     //first constructor Sample() 
     var ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, Type.EmptyTypes); 
     var obsCtor1 = typeOfCts.GetConstructors().First(c => c.GetParameters().Length == 1); 
     obsCtor1 = TypeBuilder.GetConstructor(genericTypeOfCts, obsCtor1); //hack to get close generic type ctor with typeBuilder as generic parameter 

     var generator = ctorBuilder.GetILGenerator(); 
     generator.Emit(OpCodes.Ldarg_0); //load this for base type constructor 
     generator.Emit(OpCodes.Call, typeof(object).GetConstructors().Single()); 

     generator.Emit(OpCodes.Ldarg_0); //load this for field setter 

     generator.Emit(OpCodes.Ldarg_0); //load this for ObservableTestCollection constructor 
     generator.Emit(OpCodes.Newobj, obsCtor1); //call ObservableTestCollection constructor, it will put point to new object in stack 

     generator.Emit(OpCodes.Stfld, fieldBuilder); // store into Items 
     generator.Emit(OpCodes.Ret); //return 

     //second constructor Sample(IEnumerable<Sample> source) 
     var ctorParam = typeof(IEnumerable<>).MakeGenericType(typeBuilder); 
     ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new Type[] { ctorParam }); 
     obsCtor1 = typeOfCts.GetConstructors().First(c => c.GetParameters().Length == 2); 
     obsCtor1 = TypeBuilder.GetConstructor(genericTypeOfCts, obsCtor1); //hack to get close generic type ctor with typeBuilder as generic parameter 

     generator = ctorBuilder.GetILGenerator(); 
     generator.Emit(OpCodes.Ldarg_0); //load this for base type constructor 
     generator.Emit(OpCodes.Call, typeof(object).GetConstructors().Single()); 

     generator.Emit(OpCodes.Ldarg_0); //load this for field setter 

     generator.Emit(OpCodes.Ldarg_0); //load this for ObservableTestCollection constructor 
     generator.Emit(OpCodes.Ldarg_1); //load IEnumerable for ObservableTestCollection constructor 
     generator.Emit(OpCodes.Newobj, obsCtor1); //call ObservableTestCollection constructor, it will put point to new object in stack 

     generator.Emit(OpCodes.Stfld, fieldBuilder); // store into Items 
     generator.Emit(OpCodes.Ret); //return 


     var type = typeBuilder.CreateType(); 
     var obj1 = Activator.CreateInstance(type); 

     var parameter = Activator.CreateInstance(typeof(List<>).MakeGenericType(type)); 
     var obj2 = Activator.CreateInstance(type, parameter); 
     assemblyBuilder.Save(assemblyFileName); 

Auch denken Sie bitte daran, dass ich es geschafft habe es erst zu laufen, durch ObservableTestCollection in verschiedenen Montage von Code platzieren, die Sample Klasse erzeugt.

Wenn ich mich nicht irre, generieren Sie auch ObservableTestCollection Klasse dynamisch. Es kann also funktionieren, ohne Baugruppen zu trennen, besonders wenn Sie für sie dieselben AssemblyBuilder verwenden.

+0

Danke Andrej. Es ist perfekt!! –

+0

Ich habe ObservableTestCollection erstellt und Sample mit demselben AssemblyBuilder erstellt und festgestellt, dass es funktioniert. Es ist mir auch eine große Hilfe. Danke noch einmal. –

2

Ihr Programm ist ungültig, weil Sie versuchen, eine Instanz "ObservableTestCollection of Sample" zu erstellen, aber Sample ist ein Typbuilder.

Verwenden Sie bitte TypeBuilder.GetConstructor(Type, ConstructorInfo), um einen generischen Konstruktor zu erhalten, wenn ein generisches Argument ein Typbuilder anstelle von "MakeGenericType" ist.

+0

Hallo Tony, ich versuchte mit ' var ci = TypeBuilder.GetConstructor (genericTypeOTS, ctorBuilder); ' Ich habe einen Fehler wie: ** Der angegebene Konstruktor muss für eine generische Typdefinition deklariert werden. Parametername: Konstruktor **. Ich denke, dass der Typ Sample kein generischer Typ ist, sondern "ObservableTestCollection" ein generischer Typ ist. Danke –

+0

@JoonwK zweiter Parameter sollte 'ConstructorInfo' vom offenen generischen Typ sein (' ObservableTestCollection <> '). Ich habe detaillierte und beispielhafte Erklärungen in meiner Antwort hinzugefügt. –

+0

Hallo, Sie haben bereits einen ähnlichen Fall in diesem Beitrag: http://stackoverflow.com/questions/41055488/how-to-define-a-self-referenced-type-property-with-reflection-emit-in-c -sharp –

Verwandte Themen