2009-01-14 18 views
1

J'ai récemment consulté Reflection.Emit. 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Étrange séquence de paramètres utilisant Reflection.Emit

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 je lance cet exemple, j'attendre à la sortie 1,2,3,4,5,6 cependant, il sort 2,3,4,5,6,1

Je ne sais pas trop pourquoi ... Si vous connaissez de bonnes ressources pour utiliser Reflection.Emit pourriez-vous me montrer dans cette direction. J'utilise Reflector avec l'Addit Emit.

Vive

Rohan

Répondre

5

Le problème que vous rencontrez est que vous invoquez une méthode dynamique et non statique. Votre méthode générée n'a pas de référence à l'instance de la classe Program.

Notez également que vous importez 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 bizarre que vous voyez peut-être dû à l'existence d'aucun paramètre de l'indice 6, et enroulant autour de revenir au début du tableau de paramètres. Pour plus d'informations, reportez-vous à la section "Comment: définir et exécuter des méthodes dynamiques" dans l'aide de VS.

Vous pouvez le faire fonctionner en acceptant un paramètre d'objet dans votre appel de méthode, ou la rendre statique:

Délégué public TestHandler vide (instance d'objet, 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(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); 
} 
+0

Je suis allé dans l'autre sens, mais je suppose que votre solution est meilleure que la mienne ... :-) –

1

Pour le faire fonctionner, vous devez

  1. faire Question une méthode static
  2. commentaire gen.Emit(OpCodes.Ldarg_S,6);
  3. modifier MethodInfo method1 = ... en conséquence

Une "odeur" est que l'arrêt de débogage sur le Question m ethod vous ne pouvez pas évaluer la référence this. Et cela ne devrait pas être une méthode d'instance ... ;-)

Edit: Ops. Je viens de voir la réponse de Robert Wagner qui est bien mieux expliqué que le mien. Prêt à annuler mon poste en cas de besoin ... :-)