Je jouais avec DynamicMethod et visent à faire ce qui suit:Créer DynamicMethod d'Action <T> instructions
J'ai une action dont j'obtenir le code IL en octets en utilisant GetILAsByteArray()
. A partir de ces octets, je voudrais créer une méthode dynamique et exécuter est. Voici un exemple de ce que je suis en train de faire:
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" });
}
}
Lorsque vous appelez un dynamicCallback.Invoke(null, new object[] { "World" })
nous obtenons « Exception lancée: « System.BadImageFormatException » dans mscorlib.dll ».
Une chose que je n'ai aucune idée abut est ce que je devrais utiliser comme deuxième argument pour SetCode()
, ce qui devrait être utilisé comme «maxStackSize»? Comment puis-je définir la même valeur que pour l'action initiale? Mais je suppose que ce n'est pas la raison de l'exception.
Comment puis-je créer correctement une méthode dynamique à partir des octets IL?
Solution
Ici, je voudrais résumer la solution complète fournie par Dudi Keleti:
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
}
Note: DynamicMethodHelper est classe développée par Haibo Luo et décrit dans un blog post mais peut également être téléchargé directement here.
Je pense que vous ne pouvez pas obtenir la valeur maxStackSize en utilisant la réflexion. Mais en effet, ce n'est pas le problème ici. Le problème est que l'appel 'Console.WriteLine' est codé comme un jeton de métadonnées (MethodRef probablement) et que les jetons de métadonnées ne sont valides que dans la portée du module le déclarant. Jetez un oeil aux fonctions 'DynamicILInfo.GetTokenFor', elles vont importer d'autres éléments de métadonnées et créer des jetons valides pour' DynamicMethod'. – thehennyy
@thehennyy J'ai essayé sans succès, voir mon édition. – Sjoerd222888
Vous devez remplacer l'ancien jeton du tableau d'octets IL par celui nouvellement créé que la méthode 'GetTokenFor' vous renvoie. – thehennyy