Rozumiem cel zdarzeń, szczególnie w kontekście tworzenia interfejsów użytkownika. Myślę, że to jest prototyp tworzenia zdarzenia:
public void EventName(object sender, EventArgs e);
Co robią programy obsługi zdarzeń, dlaczego są potrzebne i jak je utworzyć?
c#
.net
events
event-handling
Levi Campbell
źródło
źródło
Odpowiedzi:
Aby zrozumieć procedury obsługi zdarzeń, musisz zrozumieć delegatów . W języku C # delegata można traktować jako wskaźnik (lub odwołanie) do metody. Jest to przydatne, ponieważ wskaźnik można przekazać jako wartość.
Główną koncepcją delegata jest jego podpis lub kształt. To jest (1) typ zwracany i (2) argumenty wejściowe. Na przykład, jeśli utworzymy delegata
void MyDelegate(object sender, EventArgs e)
, może on wskazywać tylko na metody, które powrócąvoid
, i weźobject
iEventArgs
. Coś jak kwadratowy otwór i kwadratowy kołek. Mówimy więc, że metody te mają taki sam podpis lub kształt, jak delegat.Więc wiedząc, jak utworzyć odwołanie do metody, zastanówmy się nad celem zdarzeń: chcemy spowodować, aby jakiś kod został wykonany, gdy coś wydarzy się gdzie indziej w systemie - lub „obsłuży to zdarzenie”. Aby to zrobić, tworzymy określone metody dla kodu, który chcemy wykonać. Klej między zdarzeniem a metodami do wykonania to delegaci. Zdarzenie musi wewnętrznie przechowywać „listę” wskaźników do metod, które należy wywoływać, gdy zdarzenie zostanie wywołane. * Oczywiście, aby móc wywołać metodę, musimy wiedzieć, jakie argumenty przekazać do niej! Używamy delegata jako „umowy” między wydarzeniem a wszystkimi konkretnymi metodami, które zostaną wywołane.
Zatem domyślna
EventHandler
(i wiele podobnych) reprezentuje określony kształt metody (ponownie, void / object-EventArgs). Kiedy deklarujesz zdarzenie, mówisz, który kształt metody (EventHandler) to zdarzenie wywoła, określając delegata:(* To jest klucz do wydarzeń w .NET i odrywa „magię” - wydarzenie to naprawdę, pod przykryciem, tylko lista metod o tym samym „kształcie”. Lista jest przechowywana w miejscu, w którym wydarzenie żyje. Kiedy zdarzenie jest „wywoływane”, to po prostu „przejrzyj tę listę metod i wywołaj każdą z nich, używając tych wartości jako parametrów”. Przypisanie modułu obsługi zdarzeń jest po prostu ładniejszym i łatwiejszym sposobem dodania metody do tej listy metod być nazywanym).
źródło
C # zna dwa warunki
delegate
ievent
. Zacznijmy od pierwszego.Delegat
A
delegate
jest odniesieniem do metody. Tak jak możesz utworzyć odwołanie do instancji:Możesz użyć delegata, aby utworzyć odwołanie do metody:
Teraz, gdy masz już odwołanie do metody, możesz wywołać metodę za pomocą odwołania:
Ale dlaczego miałbyś Możesz także zadzwonić
myFactory.GetInstance()
bezpośrednio. W takim przypadku możesz. Istnieje jednak wiele przypadków, w których należy zastanowić się, gdzie nie chcesz, aby reszta aplikacji miała wiedzęmyFactory
lub do której należy dzwonićmyFactory.GetInstance()
bezpośrednie .Oczywistym jest jeden, jeśli chcesz być w stanie zastąpić
myFactory.GetInstance()
wmyOfflineFakeFactory.GetInstance()
jednym centralnym miejscu (aka wzorcem metody fabrycznej ).Wzór metody fabrycznej
Tak więc, jeśli masz
TheOtherClass
klasę i trzeba z niej skorzystaćmyFactory.GetInstance()
, tak będzie wyglądał kod bez delegatów (musisz poinformowaćTheOtherClass
o typie twojegomyFactory
):Jeśli chcesz skorzystać z usług delegatów, nie musisz ujawniać typu mojej fabryki:
W ten sposób możesz przekazać delegata innej klasie do użycia, bez ujawniania im swojego typu. Jedyne, co ujawniasz, to podpis twojej metody (ile masz parametrów i tym podobne).
„Podpis mojej metody”, gdzie to wcześniej słyszałem? O tak, interfejsy !!! interfejsy opisują podpis całej klasy. Pomyśl o delegatach jako o podpisie tylko jednej metody!
Inną dużą różnicą między interfejsem a delegatem jest to, że kiedy piszesz swoją klasę, nie musisz mówić C # „ta metoda implementuje ten typ delegata”. W przypadku interfejsów musisz powiedzieć „ta klasa implementuje ten typ interfejsu”.
Ponadto odwołanie do pełnomocnika może (z pewnymi ograniczeniami, patrz poniżej) odwoływać się do wielu metod (zwanych
MulticastDelegate
). Oznacza to, że po wywołaniu delegata zostanie wykonanych wiele jawnie dołączonych metod. Odwołanie do obiektu może zawsze dotyczyć tylko jednego obiektu.Ograniczenia dla a
MulticastDelegate
są takie, że podpis (metoda / delegowanie) nie powinien mieć żadnej wartości zwracanej (void
) i słów kluczowychout
iref
nie jest używany w podpisie. Oczywiście nie można wywołać dwóch metod zwracających numer i oczekiwać, że zwrócą ten sam numer. Gdy podpis jest zgodny, delegat jest automatycznie aMulticastDelegate
.Zdarzenie
Zdarzenia są tylko właściwościami (jak get; set; właściwości pól instancji), które udostępniają uczestnikowi subskrypcję z innych obiektów. Te właściwości nie obsługują jednak funkcji get; set ;. Zamiast tego obsługują dodawanie; usunąć;
Możesz mieć:
Użycie w interfejsie użytkownika (WinForms, WPF, UWP itd.)
Teraz wiemy, że delegat jest odniesieniem do metody i że możemy zorganizować wydarzenie, aby poinformować świat, że może przekazać nam swoje metody, do których można się odwołać od naszego delegata, a my jesteśmy przyciskiem interfejsu użytkownika: może poprosić każdego, kto jest zainteresowany tym, czy zostałem kliknięty, aby zarejestrować swoją metodę w naszej firmie (za pośrednictwem zdarzenia, które ujawniliśmy). Możemy użyć wszystkich tych metod, które zostały nam przekazane i odwołać się do nich przez naszego delegata. A potem poczekamy i zaczekamy .... dopóki użytkownik nie przyjdzie i nie kliknie tego przycisku, będziemy mieli wystarczający powód, aby wywołać delegata. Ponieważ delegat odwołuje się do wszystkich podanych nam metod, wszystkie te metody zostaną wywołane. Nie wiemy, co robią te metody, ani nie wiemy, która klasa implementuje te metody. Dbamy tylko o to, aby ktoś był zainteresowany kliknięciem przez nas,
Jawa
Języki takie jak Java nie mają delegatów. Zamiast tego używają interfejsów. Robią to, prosząc każdego, kto jest zainteresowany „kliknięciem nas”, o wdrożenie określonego interfejsu (za pomocą określonej metody, którą możemy wywołać), a następnie o przekazanie nam całej instancji, która implementuje interfejs. Prowadzimy listę wszystkich obiektów implementujących ten interfejs i możemy wywołać ich „pewną metodę, którą możemy wywołać” za każdym razem, gdy zostaniemy kliknięci.
źródło
event
to tylko cukier składniowy, nic więcej.Tak naprawdę jest to deklaracja dla procedury obsługi zdarzeń - metody, która zostanie wywołana, gdy zdarzenie zostanie uruchomione. Aby utworzyć wydarzenie, napiszesz coś takiego:
Następnie możesz zasubskrybować takie wydarzenie:
Z funkcją OnMyEvent () zdefiniowaną w następujący sposób:
Ilekroć
Foo
wystrzeliMyEvent
, twój przewodnikOnMyEvent
zostanie wezwany.Nie zawsze musisz używać wystąpienia
EventArgs
jako drugiego parametru. Jeśli chcesz dołączyć dodatkowe informacje, możesz użyć klasy pochodnejEventArgs
(EventArgs
jest to podstawowa konwencja). Na przykład, jeśli spojrzysz na niektóre zdarzenia zdefiniowaneControl
w WinForms lubFrameworkElement
w WPF, możesz zobaczyć przykłady zdarzeń, które przekazują dodatkowe informacje do procedur obsługi zdarzeń.źródło
OnXXX
wzorca nazewnictwa dla obsługi zdarzeń. (Głupio, OnXXX jest rozumiany jako „obsłużyć XXX” w MFC i „podnieść XXX” w .net, więc teraz jego znaczenie jest niejasne i mylące - zobacz ten post, aby poznać szczegóły ). Preferowane nazwy toRaiseXXX
wywoływanie zdarzeńHandleXXX
lub obsługaSender_XXX
zdarzeń.Oto przykładowy kod, który może pomóc:
źródło
OnMaximum?.Invoke(this,new MyEventArgs("you've entered..."));
Wystarczy dodać do istniejących świetnych odpowiedzi tutaj - bazując na kodzie w zaakceptowanym, który wykorzystuje
delegate void MyEventHandler(string foo)
...Ponieważ kompilator zna typ delegowanego zdarzenia SomethingHappened , to:
Jest całkowicie równoważny z:
Można również wyrejestrować programy obsługi w
-=
ten sposób:Dla kompletności podniesienie zdarzenia może być wykonane w następujący sposób, tylko w klasie, która jest właścicielem zdarzenia:
Potrzebna jest kopia wątku lokalny obsługi upewnić się, że wezwanie jest bezpieczny wątku - w przeciwnym razie wątek mógł pójść i wyrejestrować ostatniej obsługi zdarzenia natychmiast po tym sprawdzić, czy to było
null
, i mielibyśmy „zabawy”NullReferenceException
nie .C # 6 wprowadził ładną krótką rękę dla tego wzoru. Używa zerowego operatora propagacji.
źródło
Moje rozumienie wydarzeń jest następujące;
Delegat:
Zmienna przechowująca odniesienie do metody / metod, które mają zostać wykonane. Umożliwia to przekazywanie metod takich jak zmienna.
Kroki tworzenia i wywoływania wydarzenia:
Wydarzenie jest instancją delegata
Ponieważ zdarzenie jest instancją delegata, musimy najpierw zdefiniować delegata.
Przypisz metodę / metody, które mają zostać wykonane po uruchomieniu zdarzenia ( Wywołanie delegata )
Wystrzel wydarzenie ( Zadzwoń do delegata )
Przykład:
źródło
wydawca: gdzie zdarzają się wydarzenia. Wydawca powinien określić, z którego delegata korzysta klasa, i wygenerować niezbędne argumenty, przekazać te argumenty i siebie samemu delegatowi.
subskrybent: gdzie następuje odpowiedź. Subskrybent powinien określić metody reagowania na zdarzenia. Te metody powinny przyjmować ten sam typ argumentów co delegat. Subskrybent następnie dodaj tę metodę do delegata wydawcy.
Dlatego, gdy wydarzenie wydarzy się w wydawcy, delegat otrzyma pewne argumenty zdarzenia (dane itp.), Ale wydawca nie ma pojęcia, co się stanie z tymi wszystkimi danymi. Subskrybenci mogą tworzyć metody we własnej klasie, aby reagować na zdarzenia w klasie wydawcy, aby subskrybenci mogli odpowiadać na wydarzenia wydawcy.
źródło
źródło
Zgadzam się z KE50, z tym wyjątkiem, że widzę słowo kluczowe „event” jako alias dla „ActionCollection”, ponieważ wydarzenie zawiera zbiór działań do wykonania (tj. Delegata).
źródło
Świetne odpowiedzi techniczne w poście! Nie mam nic technicznego do dodania do tego.
Jednym z głównych powodów pojawiania się nowych funkcji w językach i oprogramowaniu jest marketing lub polityka firmy! :-) To nie może być niedoceniane!
Myślę, że dotyczy to również niektórych delegatów i wydarzeń! uważam je za przydatne i dodają wartość do języka C #, ale z drugiej strony język Java postanowił ich nie używać! zdecydowali, że cokolwiek rozwiązujesz z delegatami, możesz już rozwiązać za pomocą istniejących funkcji języka, tj. interfejsów, np
Około 2001 roku Microsoft wydał platformę .NET i język C # jako konkurencyjne rozwiązanie dla Javy, więc dobrze było mieć NOWE FUNKCJE, których Java nie ma.
źródło
Niedawno podałem przykład użycia zdarzeń w języku c # i opublikowałem go na moim blogu. Starałem się to wyjaśnić tak, jak to możliwe, na bardzo prostym przykładzie. Jeśli może to komukolwiek pomóc, oto: http://www.konsfik.com/using-events-in-csharp/
Zawiera opis i kod źródłowy (z dużą ilością komentarzy) i koncentruje się głównie na właściwym (podobnym do szablonu) wykorzystaniu zdarzeń i procedur obsługi zdarzeń.
Niektóre kluczowe punkty to:
Wydarzenia są jak „podtypy delegatów”, tylko bardziej ograniczone (w dobrym tego słowa znaczeniu). W rzeczywistości deklaracja zdarzenia zawsze zawiera delegata (EventHandlers są rodzajem delegata).
Programy obsługi zdarzeń to określone typy delegatów (możesz myśleć o nich jako o szablonie), które zmuszają użytkownika do tworzenia zdarzeń o określonej „sygnaturze”. Podpis ma format: (nadawca obiektu, EventArgs eventarguments).
Możesz stworzyć własną podklasę EventArgs, aby uwzględnić dowolny rodzaj informacji, które musi przekazać wydarzenie. Podczas korzystania ze zdarzeń nie jest konieczne używanie EventHandlerów. Możesz je całkowicie pominąć i zamiast tego użyć własnego delegata.
Jedną z kluczowych różnic między używaniem zdarzeń i delegatów jest to, że zdarzenia można wywoływać tylko z klasy, w której zostały zadeklarowane, nawet jeśli mogą być zadeklarowane jako publiczne. Jest to bardzo ważne rozróżnienie, ponieważ pozwala na ujawnienie twoich zdarzeń, dzięki czemu są one „połączone” z metodami zewnętrznymi, a jednocześnie są chronione przed „zewnętrznym niewłaściwym wykorzystaniem”.
źródło
Kolejna rzecz, o której trzeba wiedzieć , w niektórych przypadkach musisz użyć Delegatów / wydarzeń, gdy potrzebujesz niskiego poziomu sprzężenia !
Jeśli chcesz użyć komponentu w kilku miejscach w aplikacji , musisz utworzyć komponent o niskim poziomie sprzężenia, a konkretna nie zainteresowana LOGIKA musi być delegowana NA ZEWNĄTRZ komponentu! Zapewnia to, że masz oddzielony system i czystszy kod.
W zasadzie SOLID jest to „ D ” ( zasada inwersji D ).
Znany również jako „ IoC ”, odwrócenie kontroli .
Możesz zrobić „ IoC ” za pomocą zdarzeń, delegatów i DI (wstrzykiwanie zależności).
Dostęp do metody w klasie podrzędnej jest łatwy. Ale trudniej jest uzyskać dostęp do metody w klasie nadrzędnej od dziecka. Musisz przekazać rodzicowi odniesienie do dziecka! (lub użyj DI z interfejsem)
Delegaci / wydarzenia pozwalają nam komunikować się z dzieckiem do rodzica bez odniesienia!
Na powyższym schemacie nie używam funkcji Deleguj / zdarzenie, a komponent nadrzędny B musi mieć odniesienie do komponentu nadrzędnego A, aby wykonać nieuwzględnioną logikę biznesową w metodzie A. (wysoki poziom sprzężenia)
Przy takim podejściu musiałbym umieścić wszystkie referencje wszystkich komponentów korzystających z komponentu B! :(
Na powyższym schemacie używam Delegata / zdarzenia, a komponent B nie musi znać A. (niski poziom sprzężenia)
Możesz używać komponentu B w dowolnym miejscu aplikacji !
źródło