反射发射:如何为此构建构造函数 [英] Reflection Emit: how to build constructor for this
问题描述
我想动态构建的代码如下:
The code I want to build dynamically is as follow:
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);
我无法创建 Sample 的实例.
I can't create instance of Sample.
谁能帮我解决这个问题?
Can anyone help me correct this problem?
非常感谢您的帮助.
推荐答案
此错误的原因是调用了开放泛型类型的构造函数.您需要使用 TypeBuilder
作为泛型参数获取封闭泛型类型的构造函数.获取这个在这里解释的ConstructorInfo
存在一些问题.
Reason for this error is call of constructor for open generic type. You need to get constructor for closed generic type with TypeBuilder
as generic parameter.
There are some problem with getting this ConstructorInfo
that explained here.
所以解决方案是使用以下参数调用 TypeBuilder.GetConstructor(Type, ConstructorInfo)
静态方法(如@TonyTHONG 已经提到的):
So solution is to call TypeBuilder.GetConstructor(Type, ConstructorInfo)
static method (as @TonyTHONG already mentioned) with following parameters:
Type
必须是TypeBuilder
上关闭的泛型类型,在您的情况下typeof(ObservableTestCollection<>).MakeGenericType(typeBuilder)
;ConstructorInfo
您可以从开放的泛型类型中获取,在您的情况下为typeof(ObservableTestCollection<>)
.
Type
had to be generic type closed onTypeBuilder
, in your casetypeof(ObservableTestCollection<>).MakeGenericType(typeBuilder)
;ConstructorInfo
that you can get from open generic type, in your casetypeof(ObservableTestCollection<>)
.
您可以在下面查看问题的代码示例:
You can see code example for your problem below:
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
类的代码不同的程序集中来运行它.
Also please keep in mind that I've managed to run it only by placing ObservableTestCollection
in different assembly from code that generates Sample
class.
如果我没记错的话,您也在动态生成 ObservableTestCollection
类.所以它可以在不分离程序集的情况下工作,特别是如果你为它们使用相同的 AssemblyBuilder
.
If I'm not mistaken you are also generating ObservableTestCollection
class dynamically. So it may work without separating assemblies, especially if you use same AssemblyBuilder
for them.
这篇关于反射发射:如何为此构建构造函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!