Sdílet prostřednictvím


Aktualizovaný vzor událostí .NET Core

Předchozí

V předchozím článku jsme se zabývali nejběžnějšími vzory událostí. .NET Core má uvolněnější vzor. V této verzi EventHandler<TEventArgs> již definice nemá omezení, které TEventArgs musí být třída odvozena z System.EventArgs.

To zvyšuje flexibilitu pro vás a je zpětně kompatibilní. Začněme flexibilitou. Třída System.EventArgs zavádí jednu metodu: MemberwiseClone(), která vytvoří mělké kopie objektu. Tato metoda musí použít reflexi k implementaci jeho funkce pro všechny třídy odvozené z EventArgs. Tato funkce je jednodušší vytvořit v konkrétní odvozené třídě. To znamená, že odvození z System.EventArgs je omezení, které omezuje vaše návrhy, ale neposkytuje žádné další výhody. Ve skutečnosti můžete změnit definice FileFoundArgs a SearchDirectoryArgs tak, aby nebyly odvozeny z EventArgs. Program bude fungovat úplně stejně.

Můžete také změnit SearchDirectoryArgs strukturu, pokud provedete jednu další změnu:

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;
    }
}

Další změnou je volání konstruktoru bez parametrů před zadáním konstruktoru, který inicializuje všechna pole. Bez tohoto přidání by pravidla jazyka C# hlásila, že vlastnosti jsou přístupné dříve, než byly přiřazeny.

Neměli byste měnit FileFoundArgs třídu (typ odkazu) na strukturu (typ hodnoty). Důvodem je to, že protokol pro zpracování zrušení vyžaduje, aby argumenty události byly předány odkazem. Pokud jste provedli stejnou změnu, třída vyhledávání souborů nemohla nikdy sledovat žádné změny provedené žádným odběratelem události. Pro každého odběratele by se použila nová kopie struktury a tato kopie by byla jiná než kopie zobrazená objektem vyhledávání souborů.

Teď se podíváme na to, jak může být tato změna zpětně kompatibilní. Odebrání omezení nemá vliv na žádný existující kód. Všechny existující typy argumentů událostí se stále odvozují z System.EventArgs. Zpětná kompatibilita je jedním z hlavních důvodů, proč budou nadále odvozovat .System.EventArgs Předplatitelé všech stávajících událostí budou odběrateli události, která dodržovala klasický vzor.

Po podobné logice by žádný typ argumentu události vytvořený nyní neměl žádné odběratele v žádném existujícím základu kódu. Nové typy událostí, které nejsou odvozeny, System.EventArgs nebudou tyto základy kódu přerušit.

Události s odběrateli Async

Máte jeden konečný vzor, který se naučí: Jak správně psát odběratele událostí, kteří volají asynchronní kód. Výzva je popsaná v článku o asynchronní a await. Asynchronní metody můžou mít návratový typ void, ale to se důrazně nedoporučuje. Když kód odběratele události volá asynchronní metodu, nemáte na výběr, ale vytvořit metodu async void . Podpis obslužné rutiny události ho vyžaduje.

Musíte sloučit toto protichůdné pokyny. Nějakým způsobem musíte vytvořit bezpečnou async void metodu. Základy modelu, který potřebujete implementovat, jsou následující:

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.
    }
};

Nejprve si všimněte, že obslužná rutina je označena jako asynchronní obslužná rutina. Vzhledem k tomu, že je přiřazen k typu delegáta obslužné rutiny události, bude mít návratový typ void. To znamená, že musíte postupovat podle vzoru zobrazeného v obslužné rutině a nepovolit vyvolání výjimek z kontextu asynchronní obslužné rutiny. Protože nevrací úkol, neexistuje žádný úkol, který by mohl ohlásit chybu zadáním chybného stavu. Vzhledem k tomu, že metoda je asynchronní, metoda nemůže jednoduše vyvolat výjimku. (Volání metody pokračovalo provádění, protože je async.) Skutečné chování modulu runtime bude definováno odlišně pro různá prostředí. Může ukončit vlákno nebo proces, který vlastní vlákno, nebo ponechat proces v nedeterminátu stavu. Všechny tyto potenciální výsledky jsou vysoce nežádoucí.

Proto byste měli zabalit příkaz await pro asynchronní úlohu do vlastního bloku try. Pokud to způsobí chybnou úlohu, můžete chybu protokolovat. Pokud se jedná o chybu, ze které se vaše aplikace nemůže obnovit, můžete program rychle a elegantně ukončit.

Jedná se o hlavní aktualizace vzoru událostí .NET. V knihovnách, se kterými pracujete, uvidíte mnoho příkladů předchozích verzí. Měli byste ale vědět, co jsou i nejnovější vzory.

Další článek v této sérii vám pomůže rozlišovat mezi používáním delegates a events návrhy. Jsou to podobné koncepty a tento článek vám pomůže učinit nejlepší rozhodnutí pro vaše programy.

Další