Séquence de paramètres étranges utilisant Reflection.Emit
-
22-07-2019 - |
Question
J'ai consulté Reflection.Emit récemment. J'ai écrit un programme simple qui génère un DynamicMethod qui appelle simplement une autre méthode avec les mêmes paramètres
class Program
{
static void Main(string[] args)
{
Program p = new Program();
p.Test();
}
public delegate void TestHandler(int a, int b, int c, int d, int e, int f);
public void Test()
{
DynamicMethod method = new DynamicMethod(string.Empty, typeof(void), new[] { typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32) }, typeof(Program));
MethodInfo method1 = typeof(Program).GetMethod("Question",BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,null,new Type[]{typeof(Int32),typeof(Int32),typeof(Int32),typeof(Int32),typeof(Int32),typeof(Int32)},null);
MethodInfo method2 = typeof(MethodBase).GetMethod("GetCurrentMethod", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { }, null);
MethodInfo method3 = typeof(Console).GetMethod("WriteLine", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(Object) }, null);
ILGenerator gen = method.GetILGenerator();
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Ldarg_S, 0);
gen.Emit(OpCodes.Ldarg_S, 1);
gen.Emit(OpCodes.Ldarg_S, 2);
gen.Emit(OpCodes.Ldarg_S, 3);
gen.Emit(OpCodes.Ldarg_S, 4);
gen.Emit(OpCodes.Ldarg_S, 5);
gen.Emit(OpCodes.Ldarg_S, 6);
gen.Emit(OpCodes.Call, method1);
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Call, method2);
gen.Emit(OpCodes.Call, method3);
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Ret);
TestHandler handler = method.CreateDelegate(typeof(TestHandler)) as TestHandler;
handler(1, 2, 3, 4, 5, 6);
}
public void Question(int a, int b, int c, int d, int e, int f)
{
Console.WriteLine("{0},{1},{2},{3},{4},{5}", a, b, c, d, e, f);
}
}
Quand j’exécute cet exemple, il s’attendrait à produire 1,2,3,4,5,6 mais il produit 2,3,4,5,6,1
Je ne sais pas trop pourquoi ... Si vous connaissez des ressources utiles pour utiliser Reflection.Emit, pourriez-vous me diriger dans cette direction. J'utilise Reflector avec Emit AddIn.
A bientôt
Rohan
La solution
Le problème que vous rencontrez est que vous appelez une méthode dynamique, pas statique. Votre méthode générée ne fait pas référence à l'instance de la classe Program.
Notez également que vous mettez 7 paramètres sur la pile pour une méthode à 6 paramètres. Le premier paramètre doit être une référence à l'objet sur lequel vous appelez la méthode.
Le comportement étrange que vous observez est peut-être dû au fait qu'il n'y a pas de paramètre d'indice 6 et qu'il retourne au début du tableau de paramètres.
Voir "Comment: définir et exécuter des méthodes dynamiques" dans l'aide VS.
Vous pouvez le faire fonctionner en acceptant un paramètre d'objet dans votre appel de méthode ou en le rendant statique:
Délégué public void TestHandler (instance d'objet, int b, int b, int c, int d, int e, int f);
public void Test()
{
DynamicMethod method = new DynamicMethod(string.Empty, typeof(void), new[] { typeof(object), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32) }, typeof(Program));
MethodInfo method1 = typeof(Program).GetMethod("Question", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32), typeof(Int32) }, null);
MethodInfo method2 = typeof(MethodBase).GetMethod("GetCurrentMethod", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { }, null);
MethodInfo method3 = typeof(Console).GetMethod("WriteLine", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(Object) }, null);
ILGenerator gen = method.GetILGenerator();
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Ldarg_S, 0);
gen.Emit(OpCodes.Ldarg_S, 1);
gen.Emit(OpCodes.Ldarg_S, 2);
gen.Emit(OpCodes.Ldarg_S, 3);
gen.Emit(OpCodes.Ldarg_S, 4);
gen.Emit(OpCodes.Ldarg_S, 5);
gen.Emit(OpCodes.Ldarg_S, 6);
gen.Emit(OpCodes.Call, method1);
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Call, method2);
gen.Emit(OpCodes.Call, method3);
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Ret);
TestHandler handler = method.CreateDelegate(typeof(TestHandler)) as TestHandler;
handler(this, 1, 2, 3, 4, 5, 6);
}
public void Question(int a, int b, int c, int d, int e, int f)
{
Console.WriteLine("{0},{1},{2},{3},{4},{5}", a, b, c, d, e, f);
}
Autres conseils
Pour que cela fonctionne, vous devriez
- créer
Question
une méthodestatique
- comment
gen.Emit (OpCodes.Ldarg_S, 6);
- modifier
MethodInfo method1 = ...
en conséquence
Une "odeur" Si vous arrêtez le processus de débogage avec la méthode Question
, vous ne pouvez pas évaluer le code this
. Et cela ne devrait pas être pour une méthode d'instance ...; -)
Edit: Ops. Je viens de voir la réponse de Robert Wagner qui est beaucoup mieux expliquée que la mienne. Prêt à annuler mon message si nécessaire ...: -)