我正在尝试构造泛型类型的实例并在该实例上调用方法。然后返回该方法的结果。
MethodInfo methodInfo = ...;
...
var genericType = typeof(GenericType<>).MakeGenericType(typeof(TOutput));
il.Emit(OpCodes.Newobj, genericType.GetConstructor(Type.EmptyTypes));
il.Emit(OpCodes.Ldobj, methodInfo);
il.Emit(OpCodes.Callvirt, genericeClientHelper.GetMethod("MethodName", new Type[] { typeof(MethodInfo) }));
il.Emit(OpCodes.Ret);
我不断收到"System.BadImageFormatException:"Bad class token."异常。
泛型类型类如下所示
public class GenericType<T>
{
public T MethodName(MethodInfo methodInfo)
{
...
}
}
你混淆了生成的程序和生成程序之间的界限。
具体来说,生成程序在运行时构造对象的实例(MethodInfo
实例(,然后尝试生成使用该实例的程序 - 它不能,因为该实例不存在在生成的程序中,它存在于生成程序的内存中。
您必须在生成的程序中构造MethodInfo
实例 - 您必须编写 Emit 代码以生成构造MethodInfo
实例的 IL。
您尝试执行的操作与执行以下操作一样有意义:
Person person = new Person( "Antiduh", "United States" );
var genericType = typeof(GenericType<>).MakeGenericType(typeof(TOutput));
il.Emit(OpCodes.Newobj, genericType.GetConstructor(Type.EmptyTypes));
// This doesn't make sense. The object referred to by
// my `person` variable doesn't exist in the generated program.
il.Emit(OpCodes.Ldobj, person);
il.Emit(OpCodes.Callvirt, genericeClientHelper.GetMethod("MethodName", new Type[] { typeof(MethodInfo) }));
il.Emit(OpCodes.Ret);
这是第一个问题。
问题 2 是您在尝试为方法提供参数时使用了错误的操作码 -Ldobj
没有做您认为它所做的事情。
而不是使用Ldobj
,您必须通过修复生成代码的任何方式加载引用以创建内部methodInfo
。它可能是本地的,所以你最终可能会使用Ldloc
或某种形式的。
完整地循环,您收到错误"错误的类令牌"的原因是,在编译的 IL 中应该遵循Ldobj
的值应该是类元数据令牌。您提供的不是类令牌,因此出现错误。
作为演示,下面是一个完整的程序,可以模拟您正在尝试执行的操作。
private static void BuildAssembly()
{
AssemblyName assemblyName;
AssemblyBuilder assmBuilder;
ModuleBuilder modBuilder;
assemblyName = new AssemblyName( "Generated" );
// Note the save directory is the directory containing this project's solution file.
assmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
assemblyName,
AssemblyBuilderAccess.RunAndSave,
Assembly.GetExecutingAssembly().Location + @"........"
);
modBuilder = assmBuilder.DefineDynamicModule(
assemblyName.Name,
assemblyName.Name + ".dll",
true
);
/*
* public class GenericsDemo {
* }
*/
TypeBuilder typeBuilder = modBuilder.DefineType(
"Generated.GenericsDemo",
TypeAttributes.Public
);
BuildCallListMethod( typeBuilder );
typeBuilder.CreateType();
assmBuilder.Save( assemblyName.Name + ".dll" );
}
private static void BuildCallListMethod( TypeBuilder typeBuilder )
{
// public void CallList() {
// List<object> list = new List<object>();
// object thing = new object();
// list.Add(thing);
// }
var listOfObject = typeof( List<object> );
var objType = typeof( object );
// public void CallList() {
var method = typeBuilder.DefineMethod(
"CallList",
MethodAttributes.Public | MethodAttributes.HideBySig,
CallingConventions.HasThis
);
var gen = method.GetILGenerator();
// List<int> list;
var listLocal = gen.DeclareLocal( listOfObject );
listLocal.SetLocalSymInfo( "list" );
// object thing;
var thingLocal = gen.DeclareLocal( objType );
thingLocal.SetLocalSymInfo( "thing" );
// list = new List<object>();
gen.Emit( OpCodes.Newobj, listOfObject.GetConstructor( Type.EmptyTypes ) );
gen.Emit( OpCodes.Stloc_0 );
// thing = new object();
gen.Emit( OpCodes.Newobj, objType.GetConstructor( Type.EmptyTypes ) );
gen.Emit( OpCodes.Stloc_1 );
// list.Add( thing );
gen.Emit( OpCodes.Ldloc_0 ); // loads `list`.
gen.Emit( OpCodes.Ldloc_1 ); // loads `thing`.
gen.EmitCall( OpCodes.Callvirt, listOfObject.GetMethod( "Add" ), null );
gen.Emit( OpCodes.Ret );
}