Wurde bereits ein Event-Handler hinzugefügt?

183

Gibt es eine Möglichkeit festzustellen, ob einem Objekt ein Ereignishandler hinzugefügt wurde? Ich serialisiere eine Liste von Objekten in den Sitzungsstatus / aus dem Sitzungsstatus heraus, damit wir den SQL-basierten Sitzungsstatus verwenden können ... Wenn für ein Objekt in der Liste eine Eigenschaft geändert wurde, muss es markiert werden, was der Ereignishandler zuvor ordnungsgemäß erledigt hat . Wenn die Objekte jetzt deserialisiert werden, wird der Ereignishandler nicht mehr abgerufen.

In einem Anfall von leichtem Ärger habe ich gerade den Ereignishandler zur Get-Eigenschaft hinzugefügt, die auf das Objekt zugreift. Es wird jetzt aufgerufen, was großartig ist, außer dass es ungefähr fünfmal aufgerufen wird. Ich denke, der Handler wird jedes Mal hinzugefügt, wenn auf das Objekt zugegriffen wird.

Es ist wirklich sicher genug, um es einfach zu ignorieren, aber ich würde es lieber so viel sauberer machen, indem ich überprüfe, ob der Handler bereits hinzugefügt wurde, also mache ich das nur einmal.

Ist das möglich?

BEARBEITEN: Ich habe nicht unbedingt die volle Kontrolle darüber, welche Ereignishandler hinzugefügt werden. Es reicht also nicht aus, nur auf Null zu prüfen.

CodeRedick
quelle
siehe auch stackoverflow.com/questions/367523/…
Ian Ringrose

Antworten:

123

Wie von @Telos erwähnt, können Sie EventHandler von außerhalb der definierenden Klasse nur auf der linken Seite von a +=oder a verwenden -=. Wenn Sie also die definierende Klasse ändern können, können Sie eine Methode zum Durchführen der Prüfung bereitstellen, indem Sie prüfen, ob der Ereignishandler vorhanden ist. nullWenn ja, wurde kein Ereignishandler hinzugefügt. Wenn nicht, können Sie möglicherweise die Werte in Delegate.GetInvocationList durchlaufen . Wenn einer dem Delegaten entspricht, den Sie als Ereignishandler hinzufügen möchten, wissen Sie, dass er vorhanden ist.

public bool IsEventHandlerRegistered(Delegate prospectiveHandler)
{   
    if ( this.EventHandler != null )
    {
        foreach ( Delegate existingHandler in this.EventHandler.GetInvocationList() )
        {
            if ( existingHandler == prospectiveHandler )
            {
                return true;
            }
        }
    }
    return false;
}

Und dies könnte leicht geändert werden, um "den Handler hinzuzufügen, wenn er nicht da ist". Wenn Sie keinen Zugriff auf die Innereien der Klasse haben, die das Ereignis enthüllt, müssen Sie möglicherweise -=und +=, wie von @Lou Franco vorgeschlagen, erkunden.

Es ist jedoch möglicherweise besser, die Art und Weise, in der Sie diese Objekte in Betrieb nehmen und außer Betrieb nehmen, erneut zu überprüfen, um festzustellen, ob Sie keine Möglichkeit finden, diese Informationen selbst zu verfolgen.

Blair Conrad
quelle
7
Dies wird nicht kompiliert, EventHandler kann sich nur auf der linken Seite von + = oder - = befinden.
CodeRedick
2
Abstimmung nach weiterer Erklärung entfernt. SQL-Status zerstört so ziemlich die gesamte Idee hier ... :(
CodeRedick
1
Vielen Dank an Blair und SO Search, genau das, wonach ich gesucht habe (ärgerlich, dass du es nicht außerhalb der Klasse machen kannst)
George Mauer
3
Das Problem tritt meistens beim Vergleichen der Delegierten auf Gleichheit auf. Verwenden Delegate.Equals(objA, objB)Sie diese Option, wenn Sie überprüfen möchten, ob genau dieselbe Delegate vorhanden ist. Ansonsten vergleiche die Eigenschaften einzeln gerne if(objA.Method.Name == objB.Method.Name && objA.Target.GetType().FullName == objB.Target.GetType().FullName).
Sanjay
2
Dieser Code funktioniert in einer WinForm nicht. Ist es ausschließlich für ASP.NET?
JP2-Code
211

Ich bin kürzlich zu einer ähnlichen Situation gekommen, in der ich nur einmal einen Handler für eine Veranstaltung registrieren musste. Ich habe festgestellt, dass Sie die Registrierung zuerst sicher aufheben und dann erneut registrieren können, auch wenn der Handler überhaupt nicht registriert ist:

myClass.MyEvent -= MyHandler;
myClass.MyEvent += MyHandler;

Beachten Sie, dass Sie bei jeder Registrierung Ihres Handlers sicherstellen, dass Ihr Handler nur einmal registriert wird. Klingt für mich nach einer ziemlich guten Übung :)

alf
quelle
9
Scheint riskant; Wenn ein Ereignis nach dem Entfernen des Handlers und vor dem erneuten Hinzufügen ausgelöst wird, wird es übersehen.
Jimmy
27
Sicher. Du meinst, es ist nicht threadsicher. Dies kann jedoch nur dann ein Problem sein, wenn mehrere Threads oder ähnliches ausgeführt werden, was nicht üblich ist. In den meisten Fällen sollte dies der Einfachheit halber gut genug sein.
alf
6
Das stört mich. Nur weil Sie derzeit keine expliziten Threads in Ihrem Code erstellen, bedeutet dies nicht, dass nicht mehrere Threads vorhanden sind oder dass sie später nicht hinzugefügt werden. Sobald Sie (oder eine andere Person im Team, möglicherweise Monate später) einen Arbeitsthread hinzufügen oder sowohl auf die Benutzeroberfläche als auch auf die Netzwerkverbindung reagieren, öffnet sich die Tür für häufig unterbrochene Ereignisse.
Technophile
1
Ich glaube, das Zeitfenster zwischen dem Entfernen und erneuten Hinzufügen ist so klein, dass es sehr unwahrscheinlich ist, dass verpasste Ereignisse existieren, aber ja, es ist immer noch möglich.
Alisson
2
Wenn Sie dies für etwas wie das Aktualisieren einer Benutzeroberfläche usw. verwenden, ist das Risiko in Ordnung. Wenn dies für die Verarbeitung von Netzwerkpaketen usw. wäre, würde ich es nicht verwenden.
rollt
18

Wenn dies der einzige Handler ist, können Sie überprüfen, ob das Ereignis null ist. Wenn dies nicht der Fall ist, wurde der Handler hinzugefügt.

Ich denke, Sie können sicher - = bei dem Ereignis mit Ihrem Handler aufrufen, auch wenn es nicht hinzugefügt wurde (wenn nicht, können Sie es abfangen) -, um sicherzustellen, dass es nicht vorhanden ist, bevor Sie es hinzufügen.

Lou Franco
quelle
3
Diese Logik wird unterbrochen, sobald das Ereignis auch an einer anderen Stelle behandelt wird.
Bugged87
6

Dieses Beispiel zeigt, wie mit der Methode GetInvocationList () Delegaten an alle hinzugefügten Handler abgerufen werden. Wenn Sie sehen möchten, ob ein bestimmter Handler (eine bestimmte Funktion) hinzugefügt wurde, können Sie ein Array verwenden.

public class MyClass
{
  event Action MyEvent;
}

...

MyClass myClass = new MyClass();
myClass.MyEvent += SomeFunction;

...

Action[] handlers = myClass.MyEvent.GetInvocationList(); //this will be an array of 1 in this example

Console.WriteLine(handlers[0].Method.Name);//prints the name of the method

Sie können verschiedene Eigenschaften in der Method-Eigenschaft des Delegaten untersuchen, um festzustellen, ob eine bestimmte Funktion hinzugefügt wurde.

Wenn Sie sehen möchten, ob nur eine angehängt ist, können Sie einfach auf null testen.

Jason Jackson
quelle
GetInvocationList () ist kein Mitglied meiner Klasse. Tatsächlich kann ich diese Methode auf keinem Objekt oder Handler finden, auf den ich Zugriff habe ...
CodeRedick
Ich hatte das auch versucht, anscheinend kann man nur innerhalb der Klasse auf das Ereignis so zugreifen. Ich mache das generisch und wie andere bereits erwähnt haben, gehen Event-Handler wahrscheinlich sowieso verloren. Danke für die Klarstellung!
CodeRedick
4

Wenn ich Ihr Problem richtig verstehe, haben Sie möglicherweise größere Probleme. Sie sagten, dass andere Objekte diese Ereignisse möglicherweise abonnieren. Wenn das Objekt serialisiert und deserialisiert wird, verlieren die anderen Objekte (die Sie nicht kontrollieren können) ihre Ereignishandler.

Wenn Sie sich darüber keine Sorgen machen, sollte es gut genug sein, einen Verweis auf Ihren Event-Handler zu führen. Wenn Sie sich Sorgen machen, dass andere Objekte ihre Ereignishandler verlieren, sollten Sie Ihre Caching-Strategie überdenken.

CodeChef
quelle
1
D'oh! Ich hatte nicht einmal daran gedacht ... obwohl es offensichtlich gewesen sein sollte, wenn man bedenkt, dass mein ursprüngliches Problem darin bestand, dass mein eigener Handler verloren ging.
CodeRedick
2

Die einzige Möglichkeit, die für mich funktioniert hat, besteht darin, eine boolesche Variable zu erstellen, die ich beim Hinzufügen des Ereignisses auf true gesetzt habe. Dann frage ich: Wenn die Variable falsch ist, füge ich das Ereignis hinzu.

bool alreadyAdded = false;

Diese Variable kann global sein.

if(!alreadyAdded)
{
    myClass.MyEvent += MyHandler;
    alreadyAdded = true;
}
Xtian11
quelle
1

Ich stimme der Antwort von alf zu, aber wenig Modifikation ist ,, zu verwenden,

           try
            {
                control_name.Click -= event_Click;
                main_browser.Document.Click += Document_Click;
            }
            catch(Exception exce)
            {
                main_browser.Document.Click += Document_Click;
            }
Softwareentwickler
quelle
0
EventHandler.GetInvocationList().Length > 0
benPearce
quelle
2
wirft das nicht, wenn die Liste == null ist?
Boris Callens
2
Außerhalb der Klasse, die den Ereignishandler besitzt, können Sie nur - = und + = verwenden. Sie können nicht auf das Ereignis zugreifen.
Tbergelt