Czy moduł obsługi zdarzeń został już dodany?

183

Czy istnieje sposób, aby stwierdzić, czy do obiektu dodano moduł obsługi zdarzeń? Serializuję listę obiektów do / poza stan sesji, abyśmy mogli użyć stanu sesji opartego na SQL ... Gdy obiekt na liście ma zmienioną właściwość, musi zostać oflagowany, o czym obsługiwała wcześniej obsługa zdarzeń . Jednak teraz, gdy obiekty są przekształcane w postaci szeregowej, nie jest obsługiwane moduł obsługi zdarzeń.

W przypływie lekkiego rozdrażnienia właśnie dodałem moduł obsługi zdarzeń do właściwości Get, która uzyskuje dostęp do obiektu. Jest teraz wywoływany, co jest świetne, z wyjątkiem tego, że jest wywoływany 5 razy, więc myślę, że program obsługi dodaje się za każdym razem, gdy uzyskiwany jest dostęp do obiektu.

Jest to naprawdę wystarczająco bezpieczne, aby po prostu zignorować, ale wolę uczynić to o wiele czystszym, sprawdzając, czy moduł obsługi został już dodany, więc robię to tylko raz.

Czy to jest możliwe?

EDYCJA: Niekoniecznie mam pełną kontrolę nad tym, jakie procedury obsługi zdarzeń są dodawane, więc samo sprawdzenie wartości null nie jest wystarczająco dobre.

CodeRedick
źródło
patrz także stackoverflow.com/questions/367523/…
Ian Ringrose

Odpowiedzi:

123

Spoza definiującej klasy, jak wspomina @Telos, możesz używać EventHandler tylko po lewej stronie a +=lub a -=. Tak więc, jeśli masz możliwość zmodyfikowania klasy definiującej, możesz podać metodę wykonania sprawdzenia, sprawdzając, czy jest to moduł obsługi zdarzeń null- jeśli tak, to nie został dodany moduł obsługi zdarzeń. Jeśli nie, to może i możesz przeglądać wartości w Delegate.GetInvocationList . Jeśli jeden jest równy delegatowi, który chcesz dodać jako moduł obsługi zdarzeń, to wiesz, że tam jest.

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

Można to łatwo zmodyfikować, aby stało się „dodaj moduł obsługi, jeśli go nie ma”. Jeśli nie masz dostępu do wnętrz klasy, która wystawia wydarzenie, być może będziesz musiał zbadać -=i +=, zgodnie z sugestią @Lou Franco.

Lepiej jednak ponownie przeanalizuj sposób, w jaki zamawiasz i likwidujesz te obiekty, aby sprawdzić, czy nie możesz samodzielnie znaleźć sposobu na śledzenie tych informacji.

Blair Conrad
źródło
7
To się nie kompiluje, EventHandler może znajdować się tylko po lewej stronie + = lub - =.
CodeRedick
2
Usunięto głosowanie po dalszym wyjaśnieniu. Stan SQL niszczy tutaj cały pomysł ... :(
CodeRedick
1
Dzięki Blair i SO wyszukiwania, właśnie tego szukałem (denerwujące, że nie można tego zrobić poza klasą)
George Mauer
3
Problem pojawia się przez większość czasu podczas porównywania delegatów pod kątem równości. Więc użyj, Delegate.Equals(objA, objB)jeśli chcesz sprawdzić dokładnie taki sam poziom delegowania. W przeciwnym razie porównaj właściwości indywidualnie jak if(objA.Method.Name == objB.Method.Name && objA.Target.GetType().FullName == objB.Target.GetType().FullName).
Sanjay,
2
Ten kod nie działa w WinForm. Czy to jest ściśle dla ASP.NET?
jp2code
212

Niedawno doszedłem do podobnej sytuacji, w której musiałem zarejestrować program obsługi zdarzenia tylko raz. Odkryłem, że możesz najpierw bezpiecznie wyrejestrować się, a następnie zarejestrować ponownie, nawet jeśli program obsługi nie jest w ogóle zarejestrowany:

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

Pamiętaj, że robienie tego za każdym razem, gdy się rejestrujesz, zapewni, że twój przewodnik zostanie zarejestrowany tylko raz. Brzmi dla mnie całkiem niezła praktyka :)

alf
źródło
9
Wydaje się ryzykowne; jeśli zdarzenie zostanie uruchomione po usunięciu modułu obsługi i przed dodaniem go ponownie, zostanie pominięte.
Jimmy
27
Pewnie. Masz na myśli, że to nie jest bezpieczne dla wątków. Ale może to stanowić problem tylko w przypadku uruchamiania wielu wątków itp., Co nie jest normalne. W większości przypadków powinno to być wystarczające ze względu na prostotę.
alf.
6
Niepokoi mnie to. To, że obecnie nie tworzysz jawnie wątków w kodzie, nie oznacza, że ​​nie ma wielu wątków lub że nie zostaną dodane później. Gdy tylko Ty (lub ktoś w zespole, być może miesiące później) dodasz wątek roboczy lub zareagujesz zarówno na interfejs użytkownika, jak i na połączenie sieciowe, otwiera to drzwi do wysoce przerywanych wypadków.
Technophile
1
Uważam, że okno czasowe między usunięciem a ponownym dodaniem jest tak małe, że bardzo mało prawdopodobne jest istnienie pominiętych zdarzeń, ale tak, wciąż jest możliwe.
Alisson
2
Jeśli używasz tego do czegoś takiego jak aktualizacja interfejsu użytkownika itp., To jest to tak trywialne zadanie, że ryzyko jest w porządku. Gdyby to było do obsługi pakietów sieciowych itp., Nie użyłbym tego.
rzutuje
18

Jeśli jest to jedyny moduł obsługi, możesz sprawdzić, czy zdarzenie ma wartość NULL, jeśli nie jest, moduł obsługi został dodany.

Myślę, że możesz bezpiecznie zadzwonić - = na zdarzenie ze swoim operatorem, nawet jeśli nie zostało dodane (jeśli nie, możesz je złapać) - aby upewnić się, że nie ma go przed dodaniem.

Lou Franco
źródło
3
Ta logika zostanie przerwana, gdy tylko wydarzenie zostanie obsłużone w innym miejscu.
bugged87
6

Ten przykład pokazuje, jak użyć metody GetInvocationList (), aby pobrać delegatów do wszystkich dodanych procedur obsługi. Jeśli chcesz sprawdzić, czy został dodany określony moduł obsługi (funkcja), możesz użyć tablicy.

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

Możesz sprawdzić różne właściwości we właściwości Method delegata, aby sprawdzić, czy konkretna funkcja została dodana.

Jeśli chcesz sprawdzić, czy jest tylko jeden dołączony, możesz po prostu przetestować na wartość null.

Jason Jackson
źródło
GetInvocationList () nie jest członkiem mojej klasy. W rzeczywistości nie mogę znaleźć tej metody na żadnym obiekcie lub
module
Też tego próbowałem, najwyraźniej możesz uzyskać dostęp do tego wydarzenia tylko z klasy. Robię to ogólnie i, jak wspomnieli inni, obsługa zdarzeń i tak prawdopodobnie się zgubiła. Dziękuję za wyjaśnienie!
CodeRedick
4

Jeśli dobrze rozumiem twój problem, możesz mieć większe problemy. Powiedziałeś, że inne obiekty mogą subskrybować te wydarzenia. Gdy obiekt jest szeregowany i zserializowany, inne obiekty (te, nad którymi nie masz kontroli), utracą swoje procedury obsługi zdarzeń.

Jeśli nie martwisz się tym, zachowanie referencji do modułu obsługi zdarzeń powinno być wystarczające. Jeśli martwisz się skutkami ubocznymi utraty obiektów obsługi obiektów przez inne obiekty, możesz przemyśleć swoją strategię buforowania.

CodeChef
źródło
1
Nie! Nawet o tym nie pomyślałem ... chociaż powinno to być oczywiste, biorąc pod uwagę, że moim pierwotnym problemem było zgubienie mojego przewodnika.
CodeRedick
2

Jedynym sposobem, który zadziałał dla mnie, jest utworzenie zmiennej logicznej, którą ustawiłem na true podczas dodawania zdarzenia. Następnie pytam: Jeśli zmienna ma wartość false, dodaję zdarzenie.

bool alreadyAdded = false;

Ta zmienna może być globalna.

if(!alreadyAdded)
{
    myClass.MyEvent += MyHandler;
    alreadyAdded = true;
}
Xtian11
źródło
1

zgadzam się z odpowiedzią alf, ale niewielką modyfikacją jest ,, użyć,

           try
            {
                control_name.Click -= event_Click;
                main_browser.Document.Click += Document_Click;
            }
            catch(Exception exce)
            {
                main_browser.Document.Click += Document_Click;
            }
Programista
źródło
0
EventHandler.GetInvocationList().Length > 0
benPearce
źródło
2
czy to nie rzuca, gdy lista == null?
Boris Callens,
2
poza klasą będącą właścicielem modułu obsługi zdarzeń można używać tylko - = i + =. nie możesz uzyskać dostępu do wydarzenia.
tbergelt,