мне необходимо создать DynamicMethod по IL-коду следующего метода:
public int test(string v1, int v2)
{
return (int)call(new object[]{ v1, v2 });
}
где
public object call(object[] args)
{
// что-то возвращающее int
}
Я переписал код из ildasm в ILGenerator.Emit, но при выполнении сгенерированного делегата появляется ошибка недопустимого кода MSIL. Помогите, пожалуйста, создать корректные вызовы ILGenerator.Emit для генерации метода, либо пришлите ссылки на литературу по этому. Заранее благодарю!
Type[] arg_types = new Type[] { typeof(string), typeof(int) };
var dn = new DynamicMethod("myMethod", typeof(int), arg_types);
var il = dn.GetILGenerator();
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldc_I4_2);
il.Emit(OpCodes.Newarr, typeof(object));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stelem_Ref);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Ldarg_2);
il.Emit(OpCodes.Box, typeof(int));
il.Emit(OpCodes.Stelem_Ref);
il.Emit(OpCodes.Call, typeof(Form1).GetMethod("call"));
il.Emit(OpCodes.Unbox_Any, typeof(int));
il.Emit(OpCodes.Stloc_0);
il.Emit(OpCodes.Br_S);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ret);
var del = dn.CreateDelegate(typeof(Func<string, int, int>));
del.DynamicInvoke("test",77);
Прямая работа с Reflection.Emit обычно используется в особых случаях, когда нужно сгенерировать сложный многострочный метод. Для однострочного метода, который только вызывает другой метод, можно использовать более простое высокоуровневое средство - деревья выражений. При их использовании не нужно задумываться об отдельных инструкциях и корректности IL. Вот пример, также для более простого случая, когда метод call - статический:
using System;
using System.Collections;
using System.Linq.Expressions;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
public static object call(object[] args)
{
//...
}
static void Main(string[] args)
{
ParameterExpression param1 = Expression.Parameter(typeof(string), "v1");
ParameterExpression param2 = Expression.Parameter(typeof(int), "v2");
NewArrayExpression expr_arr = Expression.NewArrayInit(
typeof(object),
Expression.Convert(param1,typeof(object)),
Expression.Convert(param2, typeof(object))
);
MethodCallExpression call_expr = Expression.Call(typeof(Program).GetMethod("call"), expr_arr);
UnaryExpression conv_expr = Expression.Convert(call_expr, typeof(int));
var f_expr = Expression.Lambda<Func<string, int, int>>(conv_expr, param1,param2);
Func<string, int, int> f = f_expr.Compile();
int res = f("test", 77);
}
}
}
При этом деревья выражений внутри также используют Reflection.Emit и динамические методы. Тип времени выполнения метода f.Method будет System.Reflection.Emit.DynamicMethod+RTDynamicMethod, он также будет создан в специальной изолированной системной сборке и может быть выгружен сборщиком мусора, когда он больше не нужен.
Ответ лежит рядом. Все комментарии выше верны, но вы создаете динамический метод DynamicMethod, который не принадлежит ни одному экземпляру класса, к которому принадлежат call() и test(). Это не плохо и не хорошо: ваш метод просто висит в воздухе. То есть ваш emit создает примерно следующее:
class Test{
public static int test(string v1, int v2)
{
return (int)call(new object[]{ v1, v2 });
}
}
class Call{
public object call(object[] args)
{
// что-то возвращающее int
}
}
Такое не скомпилируется
Чтобы обратиться к методу call экземпляра вашего класса необходимо либо загрузить (либо передать) в ваш метод экземпляр класса Call (в вашем случае, это, видимо, Form1), либо создать динамический метод call подобно тому, как вы создали test, либо сделать call статическим. Последний способ наиболее простой:
class Program
{
static void Main(string[] args)
{
var a = new MyClass().test("d", 5);
Console.WriteLine(a);
Type[] arg_types = new Type[] { typeof(string), typeof(int) };
var dn = new DynamicMethod("myMethod", typeof(int), arg_types, typeof(MyClass));
var il = dn.GetILGenerator();
il.Emit(OpCodes.Ldc_I4_2);
il.Emit(OpCodes.Newarr, typeof(object));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Stelem_Ref);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Box, typeof(int));
il.Emit(OpCodes.Stelem_Ref);
il.Emit(OpCodes.Call, typeof(MyClass).GetMethod("call"));
il.Emit(OpCodes.Unbox_Any, typeof(int));
il.Emit(OpCodes.Ret);
var del = dn.CreateDelegate(typeof(Func<string, int, int>));
var r = del.DynamicInvoke("test", 77);
Console.WriteLine(r);
Console.ReadKey();
}
}
class MyClass
{
public int test(string v1, int v2)
{
var a = call(new object[] { v1, v2 });
return (int)a;
}
public static object call(object[] args)
{
// что-то возвращающее int
return 5;
}
}
Обратите внимание, что я использовал перегрузку DynamicMethod с указанием типа. Согласно документации - это тип, к открытым методам которого динамический метод будет иметь доступ. (скорее всего, myMethod будет динамически добавлен как статический метод этого типа. Но это не точно. Довольно любопытная штука).
Современные инструменты для криптотрейдинга: как технологии помогают принимать решения
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости