Preferowany sposób deklarowania zdarzeń

14

Jestem całkiem zadowolony ze zrozumienia modelu zdarzeń .NET. Myślę, że nie rozumiem małego niuansu systemu.

Kiedy zacząłem umieszczać wydarzenia na moich zajęciach, używałbym standardowego sposobu:

public event EventHandler<MyEventArgs> MyEvent;

Oznaczało to, że do subskrybowania tego wydarzenia potrzebna byłaby metoda taka jak:

void HandleThatEvent(object sender, MyEventArgs args){...}

Co jest miłe, ale okazało się, że rzadko dbam o nadawcę, więc spowodowało to rozdęcie wielu podpisów metod.

Więc przeszedłem do deklarowania własnych typów delegatów

public delegate void MyEventHandler(SomeClass argument);

Co zmniejszyło bałagan, ale pozostawiło mi mały problem, jeśli chodzi o pisanie programów obsługi:

eventImplmentor.MyEvent += HandleThatEvent;
.
.
.
void HandleThatEvent(/*oh, um, what arguments does it take? Intellisense isn't telling me*/)

Musiałbym więc wrócić do deklaracji delegata i poszukać, a następnie wrócić i napisać je lub skompilować i poczekać, aż usłyszę wiadomość.

Więc teraz zamiast, po prostu przy użyciu Action, Action<T>lub cokolwiek szablon pasuje.

public event Action<SomeClass> MyEvent;

Abym mógł najechać kursorem na zdarzenie i dowiedzieć się, jakich parametrów oczekuje.

Moje pytanie po tym wszystkim: czy istnieje najlepsza praktyka do deklarowania wydarzeń w C #? Czy powinienem wrócić do EventHandler<T>drogi, czy jest to do Action<T>przyjęcia?

Matt Ellen
źródło
Nie zapomnij upewnić się, że program obsługi jest lokalnie kopiowany do miejsca, w którym wywołałeś zdarzenie, zawsze chcesz to zrobić dla bezpieczeństwa wątków.
Snoop,
Możesz pisać własne inteligentne, złośliwe i bezpieczne zdarzenia typu w enkapsulowanym kodzie, ale wszystko, co publikujesz, powinno być zgodne ze standardowym wzorcem lub po prostu dezorientuje użytkowników twojej klasy (i najwyraźniej niektóre narzędzia).
Martin Maat,

Odpowiedzi:

8

Dla prostej, wewnętrznej obsługi zdarzeń, są takie, które po prostu wykorzystują Actionlub Action<T>, jak proponujesz. Zwykle używam standardowego wzorca, w tym Nadawcy, nawet do zdarzeń wewnętrznych, ponieważ nigdy nie wiadomo, kiedy możesz chcieć później ujawnić klasę lub zdarzenie, i nie chciałbym kary za konieczność zmiany metody zdarzenia tylko po to, aby zrobić to publiczne.

Zgadzam się z tobą, że podpis obsługi zdarzenia jest nieco cięższy niż powinien być w przypadku prostych scenariuszy, ale jest dobrze zaprojektowany do obsługi przyrostowej migracji, ponieważ z czasem mogą być potrzebne dodatkowe argumenty zdarzeń. Ogólnie rzecz biorąc, trzymałbym się standardowego wzorca, zwłaszcza, że, jak zauważyłeś, dostaniesz odpowiednią obsługę IntelliSense tylko wtedy, gdy to zrobisz.

Za ile warto, poświęciłem temu trochę czasu i wymyśliłem inny wzorzec obsługi zdarzeń : Podpis zdarzenia w .NET - Używanie silnego „nadawcy”? . Celem tutaj nie było usunięcie nadawcy, ale sprawienie, aby generalnie był on silnie wpisany jako TSenderzamiast słabo wpisany jako System.Object. Pracuje bardzo dobrze; jednak traci się wtedy obsługę IntelliSense, więc następuje niefortunny kompromis.

Ogólnie rzecz biorąc, trzymałbym się standardowego wzorca, ale interesujące jest zastanowienie się nad potencjalnie lepszymi sposobami na zrobienie tego.

Mike Rosenblum
źródło
Dziękuję za wskazanie mi twojego SO pytania. To bardzo interesujące. Nadal nie rozumiem, dlaczego tak ważne jest, aby nadawca był obowiązkowy. Przez większość czasu nie dbam o nadawcę. Czy to tylko jakaś arbitralna reguła stwardnienia rozsianego?
Matt Ellen,
Nie, oczywiście możesz zadeklarować swoich delegatów, jak chcesz. Polityka .NET zawsze obejmuje nadawcę i nie jest to wcale zły pomysł.
Neil,
@ Neil: Rozumiem, że czasem się przydaje, ale nie rozumiem zasady, aby zawsze to robić - zwłaszcza, że ​​stwardnienie rozsiane zaleca robienie wydarzeń po swojemu. Jedną z rzeczy, które bardzo lubię w wydarzeniach, jest możliwość oddzielania klas. Jeśli dołączam obiekt, to jest on ponownie łączony. Jeśli jest to tylko kwestia zgodności z CLS, mogę z tym żyć.
Matt Ellen
Jest on ponownie sprzężony ponownie tylko wtedy, gdy używasz obiektu nadawcy, w przeciwnym razie nie ma znaczenia, co zostanie ustawione jako wartość nadawcy, ponieważ go nie używasz. Zależność istnieje tylko wtedy, gdy potrzebujesz zależności. Widzę, skąd pochodzisz, a jeśli nadawca obiektu zniknie z całego kodu z dowolnego serwera na planecie, nie przespałbym nocy.
Neil,
Tak, możesz wysłać „null” jako nadawcę, jeśli naprawdę chcesz ... Ale dołączając Nadawcę, sama obsługa zdarzeń mogłaby zrezygnować z subskrypcji, gdyby tego chciała. Ogólnie rzecz biorąc, powiedziałbym, że znajomość źródła wydarzenia jest zwykle bardzo ważna.
Mike Rosenblum,