2016-12-28 6 views
3

код, который я хочу построить динамически выглядит следующим образом:Отражение Испустите: как построить конструктор для этого

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

Источник ObservableTestCollection выглядит следующим образом:

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

код я пишу :

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

Я не могу создать экземпляр образца.

Может ли кто-нибудь помочь мне исправить эту проблему?

Ваша помощь будет высоко оценена.

+0

Что вы подразумеваете под «Я не могу создать экземпляр образца»? У вас есть сообщение об ошибке во время его запуска? Ошибка компиляции или предупреждение? –

+0

Пожалуйста, отредактируйте ваш вопрос и вставьте сообщение –

+0

Сообщение, которое я получил, приведено ниже: {«Была сделана попытка загрузить программу с неправильным форматом. (Исключение из HRESULT: 0x8007000B)»} –

ответ

2

Причина этой ошибки - вызов конструктора открытого типа. Вам необходимо получить конструктор для закрытого типа с TypeBuilder как общий параметр. Есть некоторые проблемы с получением этого ConstructorInfo, который объяснил here.

Так решение вызвать TypeBuilder.GetConstructor(Type, ConstructorInfo) статический метод (как уже упоминалось @TonyTHONG) со следующими параметрами:

  • Type должен быть общий тип закрыт TypeBuilder, в вашем случае typeof(ObservableTestCollection<>).MakeGenericType(typeBuilder);
  • ConstructorInfo, который вы можете получить от открытого типа, в вашем случае typeof(ObservableTestCollection<>).

Вы можете увидеть пример кода для вашей проблемы ниже:

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

Также, пожалуйста, имейте в виду, что мне удалось запустить только путем размещения ObservableTestCollection в другой сборке из кода, который генерирует Sample класс.

Если я не ошибаюсь, вы также динамически генерируете класс ObservableTestCollection. Таким образом, он может работать без разделения сборок, особенно если вы используете для них то же самое AssemblyBuilder.

+0

Спасибо, Андрей. Это идеально!! –

+0

Я построил ObservableTestCollection и создал образец с тем же AssemblyBuilder и нашел, что он работает. Это очень помогает мне. Еще раз спасибо. –

2

Ваша программа недействительна, потому что вы пытаетесь создать экземпляр «ObservableTestCollection of Sample», но Sample - это типстроитель.

Пожалуйста, используйте TypeBuilder.GetConstructor(Type, ConstructorInfo), чтобы получить общий конструктор, если общий аргумент - это конструктор типов вместо «MakeGenericType».

+0

Привет, Тони, var ci = TypeBuilder.GetConstructor (genericTypeOTS, ctorBuilder); Я получил ошибку, например: ** Указанный конструктор должен быть объявлен в определении общего типа. Имя параметра: конструктор **. Я думаю, что тип Sample не является общим типом, но тип «ObservableTestCollection» является общим типом. Спасибо –

+0

@JoonwK второй параметр должен быть 'ConstructorInfo' из открытого типа (' ObservableTestCollection <> '). В моем ответе я добавил подробное объяснение примера. –

+0

Привет, у вас уже есть аналогичный случай в этом посте: http://stackoverflow.com/questions/41055488/how-to-define-a-self-referenced-type-property-with-reflection-emit-in-c -sharp –

Смежные вопросы