Reflection Emit:如何为此构建构造函数
Reflection Emit: how to build constructor for this
我要动态构建的代码如下:
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);
我无法创建示例实例。
谁能帮我解决这个问题?
非常感谢您的帮助。
您的程序无效,因为您尝试创建一个实例 "ObservableTestCollection of Sample" 但 Sample 是一个类型构建器。
如果泛型参数是类型构建器而不是 "MakeGenericType",请使用 TypeBuilder.GetConstructor(Type, ConstructorInfo) 获取泛型构造函数。
此错误的原因是调用了开放泛型类型的构造函数。您需要使用 TypeBuilder
作为泛型参数获取封闭泛型类型的构造函数。
获取解释 here.
的 ConstructorInfo
存在一些问题
所以解决方案是使用以下参数调用 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
class 的代码不同的程序集中来设法 运行 它。
如果我没记错的话,您也在动态生成 ObservableTestCollection
class。因此它可以在不分离程序集的情况下工作,特别是如果你对它们使用相同的AssemblyBuilder
。
我要动态构建的代码如下:
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);
我无法创建示例实例。
谁能帮我解决这个问题?
非常感谢您的帮助。
您的程序无效,因为您尝试创建一个实例 "ObservableTestCollection of Sample" 但 Sample 是一个类型构建器。
如果泛型参数是类型构建器而不是 "MakeGenericType",请使用 TypeBuilder.GetConstructor(Type, ConstructorInfo) 获取泛型构造函数。
此错误的原因是调用了开放泛型类型的构造函数。您需要使用 TypeBuilder
作为泛型参数获取封闭泛型类型的构造函数。
获取解释 here.
ConstructorInfo
存在一些问题
所以解决方案是使用以下参数调用 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
class 的代码不同的程序集中来设法 运行 它。
如果我没记错的话,您也在动态生成 ObservableTestCollection
class。因此它可以在不分离程序集的情况下工作,特别是如果你对它们使用相同的AssemblyBuilder
。