2016-09-22 28 views
1

Guten Tag! Mein Zweck ist es, eine Klasse zu implementieren, mit der wir Objekte für (von) Ereignissen subskribieren und abmelden können. Hier ist der Code meiner Klasse.Hinzufügen und Entfernen von Ereignishandler über Reflexion C#

Wie Sie hier sehen können, sind 2 öffentliche Methoden, die tatsächlich Objekte (von) Ereignis abonnieren und abmelden. Und hier ist die Probe, wie ich es testen

class Program 
{ 
    static void Main() 
    { 
     Test test = new Test(); 
     test.SubscribeTimer(); 
     while (true) 
     { 
      if(test.a == 10) 
      { 
       break; 
      } 
     } 
     test.UnsubscribeTimer(); 
     while (true) 
     { 

     } 
    } 
} 

class Test 
{ 
    System.Timers.Timer timer; 
    public int a = 0; 

    public Test() 
    { 
     timer = new System.Timers.Timer(1000); 
     timer.Start(); 
    } 

    public void SubscribeTimer() 
    { 
     var eventInfo = typeof(System.Timers.Timer).GetEvent("Elapsed"); 
     EventSubscriber.AddEventHandler(eventInfo, timer, TimerElapsed); 
     EventSubscriber.RemoveEventHandler(eventInfo, timer, TimerNotElapsed); 
    } 

    public void UnsubscribeTimer() 
    { 
     var eventInfo = typeof(System.Timers.Timer).GetEvent("Elapsed"); 
     EventSubscriber.AddEventHandler(eventInfo, timer, TimerNotElapsed); 
     EventSubscriber.RemoveEventHandler(eventInfo, timer, TimerElapsed); 
    } 

    public void TimerElapsed() 
    { 
     Console.WriteLine("timer elapsed"); 
     a++; 
    } 

    public void TimerNotElapsed() 
    { 
     Console.WriteLine("timer not elapsed"); 
     a++; 
    } 
} 

Das erwartete Verhalten der Probe ist, dass auf den Anfang werden wir die Meldung „Timer abgelaufen“ jede Sekunde, nach 10-ten Sekunde sehen wir nur sehen sollte „Timer nicht abgelaufen "und wir tun, aber wir sehen immer noch" Timer abgelaufen ". Dies bedeutet, dass die AddEventHandler-Methode funktioniert, die RemoveEventHandler-Methode jedoch nicht.

Ich würde mich sehr freuen, wenn Sie mir helfen werden. Danke im Voraus.

+0

für einen Event-Handler Um zu entfernen, muss der Delegat eine exakte Übereinstimmung sein. Gleiche Methoden- und Zieleigenschaften. Das Ziel ist nicht das Gleiche. Sehr schwer zu erkennen, das Closure-Objekt wird durch Expression.Lambda() stark verschleiert. Netter Trick, aber ich bezweifle ernsthaft, dass du es schaffen kannst. Beachten Sie außerdem, dass Sie Threading-Anforderungen verletzen. Sie können nicht davon ausgehen, dass Sie Aktualisierungen der a-Variablen sehen können. –

+0

Danke für die Antwort! Also, wenn ich die Signatur des Event-Handlers genauso machen werde wie die Signatur des Delegate-Typs des Events, kann ich mich abmelden, wie ich es in meinem Beispiel geschrieben habe? – Crispried

Antwort

0

Ich denke, es ist, weil Sie einen neuen Handler jedes Mal schaffen: (die nicht den vorherigen Handler entspricht, so kann nicht aus der Aufrufliste entfernt werden)

public static void RemoveEventHandler(EventInfo eventInfo, 
        object item, Action action) 
{ 
    var parameters = GetParameters(eventInfo); 
    var handler = GetHandler(eventInfo, action, parameters); // <-- 
    eventInfo.RemoveEventHandler(item, handler); 
} 

Warum sind Sie Wickeln der Action? Um die Parameter zu verlieren? Das Hinzufügen/Entfernen der eventInfo.RemoveEventHandler(item, action); ist aufgrund der Parameter nicht möglich. Wenn Sie einen neu generierten Handler entfernen möchten, sollten Sie diesen Handler zurückgeben, wenn Sie ihn entfernen möchten.

public static Delegate AddEventHandler(EventInfo eventInfo, object item, Action action) 
{ 
    var parameters = GetParameters(eventInfo); 
    var handler = GetHandler(eventInfo, action, parameters); 
    eventInfo.AddEventHandler(item, handler); 
    return handler; 
} 

public static void RemoveEventHandler(EventInfo eventInfo, 
        object item, Delegate handler) 
{ 
    eventInfo.RemoveEventHandler(item, handler); 
} 

var handler = EventSubscriber.AddEventHandler(eventInfo, timer, TimerElapsed); 

EventSubscriber.RemoveEventHandler(eventInfo, timer, handler); 
+0

Vielen Dank! Jetzt funktioniert es. Aber ich habe dich nicht verstanden, was du gemeint hast, als du gefragt wurdest: "Warum wickelst du die Action ein? Um welche Parameter zu verlieren? Es ist nicht möglich, den eventInfo.RemoveEventHandler (Gegenstand, Aktion) aufgrund der Parameter hinzuzufügen/zu entfernen." . Ich wäre sehr dankbar, wenn Sie es mir erklären würden, oder vielleicht können Sie mir einige Links geben, was ich gelesen habe, um es zu verstehen. – Crispried

+0

Kurz gesagt: Der Ereignistyp 'Timer.Elapsed' ist' EventHandler' und bedeutet: Wenn Sie Ereignisse empfangen möchten, muss Ihr Ereignishandler/Methode mit dem Ereignistyp übereinstimmen. 'Void MyHandler (Objekt Absender, EventArgs e)'. Ihre Aktion stimmt nicht mit der Definition überein. Wenn Sie also 'Expression.Lambda' verwenden, erstellen Sie eine neue Methode, die der Definition des Ereignisses' Timer.Elapsed' entspricht und Ihre parameterlose 'Aktion' aufruft. So wird es deine Aktion umhüllen. Die Sache ist, dass Sie den Delegierten abmelden müssen, der aus dem 'Expression.Lambda' erstellt wurde, anstelle von der Action it self, da dieser der Invocationsliste hinzugefügt wurde. –

+0

Also, es ist gut, wenn wir unsere Parameter (Objekt-Sender und EventArgs e) nicht verwenden wollen, weil unser Code (ich meine unser Event-Handler) (zumindest für mich) klarer aussieht. Aber wenn ich diese Parameter in meinem Event-Handler verwenden möchte, sollte ich diese Methode mit notwendigen Paraememern erstellen und in EventSubscriber.AddEventHandler() werde ich meine Action (nicht delegate created from the Expression.Lambda) abonnieren. Wenn die Signatur des Handlers der Signatur des Delegierten entspricht, kann ich mich abmelden, wie ich in meinem Beispiel geschrieben habe. Habe ich recht? – Crispried

Verwandte Themen