Udostępnij za pośrednictwem


Zaktualizowany wzorzec zdarzeń platformy .NET Core

Poprzedni

W poprzednim artykule omówiono najczęstsze wzorce zdarzeń. Platforma .NET Core ma bardziej zrelaksowany wzorzec. W tej wersji definicja EventHandler<TEventArgs> nie ma już ograniczenia, że TEventArgs musi być klasą pochodzącą z System.EventArgs.

Zwiększa to elastyczność dla ciebie i jest wstecznie kompatybilne. Zacznijmy od elastyczności. Implementacja dla System.EventArgs używa metody zdefiniowanej w System.Object, jednej metody: MemberwiseClone(), która tworzy płytką kopię obiektu. Ta metoda musi używać odbicia w celu zaimplementowania jej funkcji dla dowolnej klasy pochodzącej z EventArgs. 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 FileFoundArgs i SearchDirectoryArgs, aby nie pochodziły one z EventArgs. Program działa dokładnie tak samo.

Możesz również zmienić SearchDirectoryArgs na strukturę, pod warunkiem, że 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ą dostępne przed przypisaniem.

Nie należy zmieniać FileFoundArgs z klasy (typu odwołania) na strukturę (typ wartości). Procedura obsługi anulowania wymaga przekazania argumentów zdarzeń przez odwołanie. Jeśli wprowadzisz tę samą zmianę, klasa wyszukiwania plików nigdy nie mogłaby zaobserwować jakichkolwiek zmian wprowadzonych przez jakiegokolwiek 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 System.EventArgs. Zgodność z poprzednimi wersjami jest jednym z głównych powodów, dla których nadal wywodzą się z System.EventArgs. Wszyscy istniejący subskrybenci zdarzeń są 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ń, które nie pochodzą z System.EventArgs, nie przerywają tych baz kodu.

Zdarzenia z asynchronicznymi subskrybentami

Masz jeden ostateczny wzorzec do nauki: Jak poprawnie pisać subskrybentów zdarzeń, którzy wywołują kod asynchroniczny. Wyzwanie zostało opisane w artykule dotyczącym async i await. Metody asynchroniczne mogą mieć typ zwracania void, ale jest to odradzane. Gdy kod subskrybenta zdarzenia wywołuje metodę asynchroniczną, nie masz wyboru, musisz utworzyć metodę async void. Sygnatura programu obsługi zdarzeń tego wymaga.

Musisz uzgodnić te przeciwstawne wskazówki. W jakiś sposób musisz utworzyć bezpieczną metodę async void. Podstawowe informacje o wzorcu, który należy zaimplementować, są wyświetlane w następującym kodzie:

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ż przypisuje się go do typu delegata obsługi zdarzeń, ma typ zwracany void. Oznacza to, że należy postępować zgodnie ze wzorcem pokazanym w handlerze i nie zezwalać na rzucenie żadnych wyjątków poza kontekst asynchronicznego handlera. Ponieważ nie zwraca zadania, nie ma zadania, które mogłoby zgłosić błąd poprzez przejście do stanu uszkodzonego. Ponieważ metoda jest asynchroniczna, nie będzie mogła zgłosić wyjątku. (Metoda wywołująca kontynuuje wykonywanie, ponieważ jest async). Rzeczywiste zachowanie środowiska uruchomieniowego jest definiowane inaczej dla różnych środowisk. Może to 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.

Należy opakowować wyrażenie await dla asynchronicznego zadania we własnym bloku try. Jeśli spowoduje to przerwane zadanie, możesz zarejestrować błąd. Jeśli jest to błąd, z którego aplikacja nie może odzyskać sprawności, możesz szybko i bezpiecznie zamknąć program

W tym artykule wyjaśniono główne aktualizacje wzorca zdarzeń platformy .NET. W bibliotekach, w których pracujesz, możesz zobaczyć wiele przykładów wcześniejszych wersji. Należy jednak zrozumieć, jakie są najnowsze wzorce. Gotowy kod przykładu można zobaczyć na stronie Program.cs.

Następny artykuł z tej serii ułatwia rozróżnienie między używaniem delegates i events w projektach. Są to podobne koncepcje, a ten artykuł pomoże Ci podjąć najlepszą decyzję dla Twoich programów.

Następne