Dela via


Det uppdaterade händelsemönstret för .NET Core

föregående

I föregående artikel diskuterades de vanligaste händelsemönstren. .NET Core har ett mer avslappnat mönster. I den här versionen har EventHandler<TEventArgs> definitionen inte längre villkoret att TEventArgs måste vara en klass som härleds från System.EventArgs.

Detta ökar flexibiliteten för dig och är bakåtkompatibelt. Vi börjar med flexibiliteten. Implementeringen för System.EventArgs använder en metod som definierats i System.Object en metod: MemberwiseClone(), som skapar en ytlig kopia av objektet. Metoden måste använda reflektion för att implementera dess funktioner för alla klasser som härleds från EventArgs. Den funktionen är enklare att skapa i en specifik härledd klass. Det innebär i praktiken att härleda från System.EventArgs är en begränsning som begränsar dina design, men ger ingen extra fördel. Du kan faktiskt ändra definitionerna för FileFoundArgs och SearchDirectoryArgs så att de inte härleds från EventArgs. Programmet fungerar exakt likadant.

Du kan också ändra SearchDirectoryArgs till en struct om du gör en ändring till:

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

Den extra ändringen är att anropa den parameterlösa konstruktorn innan du anger konstruktorn som initierar alla fält. Utan detta skulle C#-reglerna rapportera att egenskaperna används innan de tilldelas.

Du bör inte ändra FileFoundArgs från en klass (referenstyp) till en struct (värdetyp). Protokollet för hantering av avbryt kräver att du skickar händelseargument med referens. Om du har gjort samma ändring kunde filsökningsklassen aldrig observera några ändringar som gjorts av någon av händelseprenumeranterna. En ny kopia av strukturen skulle användas för varje prenumerant och kopian skulle vara en annan kopia än den som visas av filsökningsobjektet.

Nu ska vi fundera på hur den här ändringen kan vara bakåtkompatibel. Borttagningen av villkoret påverkar inte någon befintlig kod. Alla befintliga händelseargumenttyper härleds fortfarande från System.EventArgs. Bakåtkompatibilitet är en viktig orsak till att de fortsätter att härledas från System.EventArgs. Alla befintliga händelseprenumeranter är prenumeranter på en händelse som följde det klassiska mönstret.

Efter liknande logik skulle alla händelseargumenttyper som skapats nu inte ha några prenumeranter i några befintliga kodbaser. Nya händelsetyper som inte härleds från System.EventArgs bryter inte dessa kodbaser.

Händelser med Async-prenumeranter

Du har ett sista mönster att lära dig: Så här skriver du händelseprenumeranter som anropar asynkron kod korrekt. Utmaningen beskrivs i artikeln om asynkronisering och väntar på. Asynkrona metoder kan ha en typ av ogiltig retur, men det rekommenderas inte. När din händelseprenumerantkod anropar en asynkron metod har du inget annat val än att skapa en async void-metod. Händelsehanterarsignaturen kräver det.

Du måste förlika den här motsägande instruktionen. På något sätt måste du skapa en säker async void-metod. Grunderna i det mönster som du behöver implementera visas i följande kod:

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

Observera först att hanteraren har markerats som en asynkron hanterare. Eftersom den tilldelas till en händelsehanterardelegattyp har den en void-returtyp. Det innebär att du måste följa mönstret som visas i hanteraren och inte tillåta några undantag att kastas ut ur den asynkrona hanterarens kontext. Eftersom den inte returnerar en uppgift finns det ingen uppgift som kan rapportera felet genom att gå in i feletillståndet. Eftersom metoden är asynkron kan metoden inte utlösa undantaget. (Anropsmetoden fortsätter körningen eftersom den är async.) Det faktiska körningsbeteendet definieras på olika sätt för olika miljöer. Den kan avsluta tråden eller processen som äger tråden eller lämna processen i ett obestämt tillstånd. Alla dessa potentiella resultat är mycket oönskade.

Du bör omsluta await-uttrycket för asynkron uppgift i ditt eget försöksblock. Om det orsakar en felaktig uppgift kan du logga felet. Om det är ett fel som programmet inte kan återställas från kan du avsluta programmet snabbt och smidigt

I den här artikeln beskrivs de viktigaste uppdateringarna av .NET-händelsemönstret. Du kan se många exempel på tidigare versioner i de bibliotek som du arbetar med. Du bör dock också förstå vilka de senaste mönstren är. Du kan se den färdiga koden för exemplet på Program.cs.

Nästa artikel i den här serien hjälper dig att skilja mellan att använda delegates och events i din design. De är liknande begrepp och den artikeln hjälper dig att fatta det bästa beslutet för dina program.

Nästa