Dela via


Det uppdaterade .NET Core-händelsemönstret

Föregående

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

Detta ökar flexibiliteten för dig och är bakåtkompatibelt. Vi börjar med flexibiliteten. Klassen System.EventArgs introducerar en metod: MemberwiseClone(), som skapar en ytlig kopia av objektet. Den 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 inte ger någon ytterligare fördel. I själva verket kan du ä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 ytterligare ä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 har tilldelats.

Du bör inte ändra FileFoundArgs från en klass (referenstyp) till en struct (värdetyp). Det beror på att protokollet för hantering av avbryt kräver att händelseargumenten skickas 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 kommer att vara 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äntan. 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 stämma av den här motsatta vägledningen. 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 finns nedan:

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 typ av ogiltig retur. Det innebär att du måste följa mönstret som visas i hanteraren och inte tillåta att några undantag utlöses från asynkront hanterarens kontext. Eftersom den inte returnerar en aktivitet finns det ingen uppgift som kan rapportera felet genom att ange det felaktiga tillståndet. Eftersom metoden är asynkron kan metoden inte bara utlösa undantaget. (Anropsmetoden har fortsatt körning 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.

Därför bör du omsluta await-instruktionen för asynkron uppgift i ditt eget try-block. 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

Det här är de viktigaste uppdateringarna av .NET-händelsemönstret. Du ser 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.

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

Nästa