2010-09-02 2 views
1

Bonjour toutPourquoi une exception Argument est-elle générée lors de la création dynamique du gestionnaire d'événements?

J'ai écrit la méthode suivante:

private void RegisterEvent(object targetObject, string eventName, string methodName) 
{ 
    EventInfo eventInfo = targetObject.GetType().GetEvent(eventName); 
    MethodInfo method = eventInfo.EventHandlerType.GetMethod("Invoke"); 
    IEnumerable<Type> types = method.GetParameters().Select(param => param.ParameterType); 

    DynamicMethod dynamicMethod = new DynamicMethod(eventInfo.EventHandlerType.Name, typeof (void), types.ToArray(), typeof (QueryWindow)); 
    MethodInfo methodInfo = typeof (QueryWindow).GetMethod(methodName, new[] { typeof (object) }); 

    ILGenerator ilGenerator = dynamicMethod.GetILGenerator(256); 
    ilGenerator.Emit(OpCodes.Ldarg_1); 
    ilGenerator.EmitCall(OpCodes.Call, methodInfo, null); 

    dynamicMethod.DefineParameter(1, ParameterAttributes.In, "sender"); 
    dynamicMethod.DefineParameter(2, ParameterAttributes.In, "e"); 

    // Get an argument exception here 
    Delegate methodDelegate = dynamicMethod.CreateDelegate(eventInfo.EventHandlerType, this); 
    eventInfo.AddEventHandler(targetObject, methodDelegate); 
} 

Je reçois ArgumentException avec le message

Erreur de liaison à la méthode cible.

dans la ligne

Delegate methodDelegate = dynamicMethod.CreateDelegate(eventInfo.EventHandlerType, this); 

Quelqu'un pourrait-il indiquer sur mon erreur?

Merci d'avance.

+3

Où obtenez-vous l'exception (à quelle ligne) et quel est le message? –

+0

Merci pour la réponse Je reçois "Erreur de liaison à la méthode cible". dans la ligne Delegate methodDelegate = dynamicMethod.CreateDelegate (eventInfo.EventHandlerType, this); – user306080

Répondre

1

En supposant que methodName est une méthode statique de QueryWindow, cela devrait fonctionner:

private static void RegisterEvent(object targetObject, string eventName, string methodName) 
{ 
    var eventInfo = targetObject.GetType().GetEvent(eventName); 
    var method = eventInfo.EventHandlerType.GetMethod("Invoke"); 
    var types = method.GetParameters().Select(param => param.ParameterType); 

    var methodInfo = typeof(QueryWindow).GetMethod(methodName, new[] { typeof(object) }); 

    // replaced typeof(void) by null  
    var dynamicMethod = new DynamicMethod(eventInfo.EventHandlerType.Name, null, types.ToArray(), typeof(QueryWindow)); 

    ILGenerator ilGenerator = dynamicMethod.GetILGenerator(256); 

    // Using Ldarg_0 to pass the sender to methodName ; Ldarg_1 to pass the event args 
    ilGenerator.Emit(OpCodes.Ldarg_0); 
    ilGenerator.EmitCall(OpCodes.Call, methodInfo, null); 

    // Added return 
    ilGenerator.Emit(OpCodes.Ret); 

    // Removed parameter definition (implicit from DynamicMethod constructor) 

    // Removed the target argument 
    var methodDelegate = dynamicMethod.CreateDelegate(eventInfo.EventHandlerType); 
    eventInfo.AddEventHandler(targetObject, methodDelegate); 
} 

Edit:

Comme vous pouvez utiliser .NET 3.5, vous devez créer un arbre d'expression. Voici une autre solution:

public class QueryWindow 
{ 
    public void RegisterEvent(object targetObject, string eventName, string methodName) 
    { 
     var eventInfo = targetObject.GetType().GetEvent(eventName); 
     var sender = Expression.Parameter(typeof (object), "sender"); 
     var e = Expression.Parameter(typeof (EventArgs), "e"); 
     var body = Expression.Call(Expression.Constant(this), methodName, null, e); 
     var lambda = Expression.Lambda(eventInfo.EventHandlerType, body, sender, e); 
     eventInfo.AddEventHandler(targetObject, lambda.Compile()); 
    } 

    public void OnEvent(object o) 
    { 
     Console.WriteLine(o); 
    } 
} 

Notez que la méthode OnEvent n'est plus statique. Je suppose également que les événements auxquels vous essayez de vous abonner sont des événements qui suivent les conventions .NET (expéditeurs + événements). De cette façon, nous pouvons tirer parti de la contravariance et toujours passer un lambda de type:

(object sender, EventArgs e) => { /* */ } 
+0

Merci, ça marche. Et comment dois-je modifier la méthode dans le cas où methodName n'est pas la méthode statique (c'est-à-dire qu'elle devrait modifier certaines propriétés)? – user306080

+1

Si la méthode n'est pas statique, vous devez insérer une instance de QueryWindow sur la pile avant d'appuyer sur arg0 (ou arg1) et d'effectuer l'appel. Cela peut être problématique car vous devrez certainement passer cette instance en argument de la méthode dynamique. Si vous postez plus de détails sur ce scénario particulier dans votre question, je pourrais mettre à jour ma réponse en conséquence. –

+0

Merci encore pour l'aide L'idée est de mettre à jour le RichTextBox avec les données stockées dans EventArgs. Il existe la méthode à appeler (en essayant de le faire fonctionner sans ajouter de statique): public void PrintMessage (objet obj) { MéthodeInfo méthodeInfo = obj.GetType(). GetProperty ("Message"). GetGetMethod(); Paragraphe paragraphe = nouveau Paragraphe(); paragraph.Inlines.Add (methodInfo.Invoke (obj, null) .ToString()); //MessageBox.Show(methodInfo.Invoke(obj, null) .ToString()); box.Document.Blocks.Add (paragraphe); } – user306080

1

Lorsque vous appelez le DynamicMethod.CreateDelegate, vous ne devez pas passer un paramètre cible.

Edit:

Je pense que vous auriez aussi de faire le premier paramètre = 0, et changer le codegen en conséquence.

4

dynamicMethod.CreateDelegate (eventInfo.EventHandlerType, this);

Le cet argument ne peut pas être correct. Cela fait référence à votre classe, celle qui génère le type dynamique. Sûrement vous avez besoin targetObject à la place.

+0

Ca pourrait marcher aussi :) – leppie

Questions connexes