Zaktualizowany wzorzec zdarzeń platformy .NET Core
W poprzednim artykule omówiono najczęstsze wzorce zdarzeń. Platforma .NET Core ma bardziej zrelaksowany wzorzec. W tej wersji EventHandler<TEventArgs>
definicja nie ma już ograniczenia, które TEventArgs
muszą być klasą pochodzącą z System.EventArgs
klasy .
Zwiększa to elastyczność i jest zgodna z poprzednimi wersjami. Zacznijmy od elastyczności. Klasa System.EventArgs wprowadza jedną metodę: MemberwiseClone()
, która tworzy płytkią kopię obiektu.
Ta metoda musi używać odbicia w celu zaimplementowania jej funkcji dla dowolnej klasy pochodzącej z EventArgs
klasy . Ta funkcja jest łatwiejsza do utworzenia w określonej klasie pochodnej. Oznacza to, że wyprowadzanie z elementu System.EventArgs jest ograniczeniem ograniczającym projekty, ale nie zapewnia żadnych dodatkowych korzyści.
W rzeczywistości można zmienić definicje elementu FileFoundArgs
i SearchDirectoryArgs
tak, aby nie pochodziły one z klasy EventArgs
.
Program będzie działał dokładnie tak samo.
Możesz również zmienić SearchDirectoryArgs
element na strukturę, jeśli wprowadzisz jeszcze jedną zmianę:
internal struct SearchDirectoryArgs
{
internal string CurrentSearchDirectory { get; }
internal int TotalDirs { get; }
internal int CompletedDirs { get; }
internal SearchDirectoryArgs(string dir, int totalDirs, int completedDirs) : this()
{
CurrentSearchDirectory = dir;
TotalDirs = totalDirs;
CompletedDirs = completedDirs;
}
}
Dodatkową zmianą jest wywołanie konstruktora bez parametrów przed wprowadzeniem konstruktora, który inicjuje wszystkie pola. Bez tego dodatku reguły języka C# zgłaszałyby, że właściwości są uzyskiwane przed ich przypisaniem.
Nie należy zmieniać FileFoundArgs
klasy (typu odwołania) na strukturę (typ wartości). Dzieje się tak, ponieważ protokół do obsługi anulowania wymaga przekazania argumentów zdarzeń przez odwołanie. Jeśli wprowadzisz tę samą zmianę, klasa wyszukiwania plików nigdy nie może obserwować żadnych zmian wprowadzonych przez żadnego z subskrybentów zdarzeń. Nowa kopia struktury będzie używana dla każdego subskrybenta, a kopia będzie inną kopią niż kopia widoczna przez obiekt wyszukiwania plików.
Następnie zastanówmy się, jak ta zmiana może być zgodna z poprzednimi wersjami.
Usunięcie ograniczenia nie ma wpływu na żaden istniejący kod. Wszystkie istniejące typy argumentów zdarzeń nadal pochodzą z klasy System.EventArgs
.
Zgodność z poprzednimi wersjami jest jednym z głównych powodów, dla których będą nadal pochodzić z klasy System.EventArgs
. Wszyscy istniejący subskrybenci zdarzeń będą subskrybentami zdarzenia, które było zgodne ze wzorcem klasycznym.
Zgodnie z podobną logiką każdy utworzony typ argumentu zdarzenia nie miałby teraz żadnych subskrybentów w żadnych istniejących bazach kodu. Nowe typy zdarzeń, z których nie pochodzą System.EventArgs
, nie spowodują przerwania tych baz kodu.
Zdarzenia z subskrybentami asynchronicznych
Masz jeden ostateczny wzorzec do nauki: Jak poprawnie pisać subskrybentów zdarzeń, którzy nazywają kod asynchroniczny. Wyzwanie zostało opisane w artykule dotyczącym asynchronicznego i await. Metody asynchroniczne mogą mieć typ zwracany void, ale jest to zdecydowanie zniechęcone. Gdy kod subskrybenta zdarzenia wywołuje metodę asynchroniową, nie masz wyboru, ale utworzysz metodę async void
. Podpis programu obsługi zdarzeń wymaga go.
Musisz uzgodnić te przeciwstawne wskazówki. W jakiś sposób musisz utworzyć bezpieczną async void
metodę. Poniżej przedstawiono podstawowe informacje o wzorcu, który należy zaimplementować:
worker.StartWorking += async (sender, eventArgs) =>
{
try
{
await DoWorkAsync();
}
catch (Exception e)
{
//Some form of logging.
Console.WriteLine($"Async task failure: {e.ToString()}");
// Consider gracefully, and quickly exiting.
}
};
Najpierw zwróć uwagę, że procedura obsługi jest oznaczona jako procedura obsługi asynchroniczne. Ponieważ jest on przypisywany do typu delegata procedury obsługi zdarzeń, będzie miał typ zwracany void. Oznacza to, że należy postępować zgodnie ze wzorcem pokazanym w procedurze obsługi i nie zezwalać na wyrzucenie żadnych wyjątków z kontekstu programu obsługi asynchronicznego. Ponieważ nie zwraca zadania, nie ma zadania, które może zgłosić błąd, wprowadzając stan błędu. Ponieważ metoda jest asynchronizuj, metoda nie może po prostu zgłosić wyjątku. (Metoda wywołująca kontynuowała wykonywanie, ponieważ jest async
.) Rzeczywiste zachowanie środowiska uruchomieniowego będzie definiowane inaczej dla różnych środowisk. Może zakończyć wątek lub proces, który jest właścicielem wątku, lub pozostawić proces w nieokreślonym stanie. Wszystkie te potencjalne wyniki są wysoce niepożądane.
Dlatego należy opakowować instrukcję await dla asynchronicznego zadania we własnym bloku try. Jeśli spowoduje to błąd, możesz zarejestrować błąd. Jeśli jest to błąd, z którego aplikacja nie może odzyskać, możesz zamknąć program szybko i bezpiecznie
Są to główne aktualizacje wzorca zdarzeń platformy .NET. W bibliotekach, z których pracujesz, zobaczysz wiele przykładów wcześniejszych wersji. Należy jednak zrozumieć, jakie są najnowsze wzorce.
Następny artykuł z tej serii ułatwia rozróżnienie między użyciem i delegates
events
w projektach. Są to podobne pojęcia i ten artykuł pomoże Ci podjąć najlepszą decyzję dotyczącą programów.