Вы путаете границу между генерируемой программой и генерирующей программой.
В частности, ваша генерирующая программа при запуске конструирует экземпляр объекта (экземпляр MethodInfo
), а затем пытается сгенерировать программу, которая использует этот экземпляр, но не может, потому что этот экземпляр не существует в сгенерированной программе. существует в памяти генерирующей программы.
Вы должны создать экземпляр MethodInfo
внутри сгенерированной программы — вы должны написать код Emit для создания IL, который создает экземпляр MethodInfo
.
То, что вы пытаетесь сделать, имеет такой же смысл, как и следующее:
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);
Это проблема номер 1.
Проблема номер 2 заключается в том, что вы используете неправильный код операции при попытке предоставить аргумент методу - Ldobj
не делает того, что вы думаете.
Вместо использования Ldobj
вам придется загрузить ссылку любым способом, который вы исправите в коде генерации, чтобы создать внутренний файл methodInfo
. Это, вероятно, будет локальным, поэтому вы, вероятно, в конечном итоге будете использовать Ldloc
или какую-то его форму.
Чтобы завершить круг, причина, по которой вы получаете сообщение об ошибке «Плохой токен класса», заключается в том, что значение, которое должно следовать за Ldobj
в скомпилированном IL, должно быть токеном метаданных класса. То, что вы предоставили, не было токеном класса, отсюда и ошибка.
В качестве демонстрации ниже приведена полная программа, которая эмулирует то, что вы пытаетесь сделать.
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 );
}
person
antiduh
schedule
27.07.2017
genericeClientHelper.GetMethod
, вероятно, должно бытьgenericType.GetMethod
, я думаю. - person Rob   schedule 27.07.2017