Я играю с DynamicMethod и стремиться сделать следующее:Создание DynamicMethod от действий <T> инструкции
У меня есть Action, от которого я получить код IL, как байты с помощью GetILAsByteArray()
. Из этих байтов я хотел бы создать динамический метод и выполнить. Вот пример того, что я пытаюсь сделать:
class Program
{
static void Main(string[] args)
{
//Create action and execute
Action<string> myAction = s =>
{
Console.WriteLine("Hello " + s);
};
myAction("World");
//Get IL bytes
byte[] ilBytes = myAction.GetMethodInfo().GetMethodBody().GetILAsByteArray();
DynamicMethod dynamicCallback = new DynamicMethod("myAction", typeof(void), new Type[] { typeof(string) });
DynamicILInfo dynamicIlInfo = dynamicCallback.GetDynamicILInfo();
dynamicIlInfo.SetCode(ilBytes, 100);
dynamicCallback.Invoke(null, new object[] { "World" });
}
}
При вызове dynamicCallback.Invoke(null, new object[] { "World" })
мы получаем «Exception брошенного:„System.BadImageFormatException“в mscorlib.dll».
Одна вещь, о которой я понятия не имею, - это то, что я должен использовать в качестве второго аргумента для SetCode()
, что следует использовать как «maxStackSize»? Как установить то же значение, что и для начального действия? Но я полагаю, что это не причина исключения.
Как правильно создать динамический метод из байтов IL?
Решение
Здесь я хотел бы суммировать полное решение, представленное Дуди Келети:
static void Main(string[] args)
{
Action<string> myAction = s =>
{
Console.WriteLine("Hello " + s);
};
MethodInfo method = myAction.GetMethodInfo();
object target = myAction.Target;
DynamicMethod dm = new DynamicMethod(
method.Name,
method.ReturnType,
new[] {method.DeclaringType}.
Concat(method.GetParameters().
Select(pi => pi.ParameterType)).ToArray(),
method.DeclaringType,
skipVisibility: true);
DynamicILInfo ilInfo = dm.GetDynamicILInfo();
var body = method.GetMethodBody();
SignatureHelper sig = SignatureHelper.GetLocalVarSigHelper();
foreach (LocalVariableInfo lvi in body.LocalVariables)
{
sig.AddArgument(lvi.LocalType, lvi.IsPinned);
}
ilInfo.SetLocalSignature(sig.GetSignature());
byte[] code = body.GetILAsByteArray();
ILReader reader = new ILReader(method);
DynamicMethodHelper.ILInfoGetTokenVisitor visitor = new DynamicMethodHelper.ILInfoGetTokenVisitor(ilInfo, code);
reader.Accept(visitor);
ilInfo.SetCode(code, body.MaxStackSize);
dm.Invoke(target, new object[] { target, "World" });
Console.ReadLine(); //Just to see the result
}
Примечание: DynamicMethodHelper является класс, разработанный Хайбо Л и описал в blog post но также можно загрузить напрямую here.
Я думаю, что вы не можете получить значение maxStackSize, используя отражение. Но на самом деле это не проблема. Проблема заключается в том, что вызов Console.WriteLine кодируется как токен метаданных (вероятно, MethodRef), а токены метаданных действительны только в области модуля, объявляющего его. Посмотрите на функции DynamicILInfo.GetTokenFor, они будут импортировать другие элементы метаданных и создавать токены, действующие для 'DynamicMethod'. – thehennyy
@thehennyy Я пробовал без успеха, вижу мое редактирование. – Sjoerd222888
Вы должны заменить старый токен в массиве байтов IL новым созданным, который возвращает метод GetTokenFor'. – thehennyy