Das aktualisierte .NET Core-EreignismusterThe Updated .NET Core Event Pattern

VorherigePrevious

Im vorherigen Artikel wurden die am häufigsten verwendeten Ereignismuster erläutert.The previous article discussed the most common event patterns. .NET Core ist ein lockereres Muster..NET Core has a more relaxed pattern. Die EventHandler<TEventArgs>-Definition hat in dieser Version nicht länger die Einschränkung, dass TEventArgs eine von System.EventArgs abgeleitete Klasse sein muss.In this version, the EventHandler<TEventArgs> definition no longer has the constraint that TEventArgs must be a class derived from System.EventArgs.

Dies erhöht die Flexibilität für Sie und ist abwärtskompatibel.This increases flexibility for you, and is backwards compatible. Beginnen wir mit der Flexibilität.Let's start with the flexibility. Die System.EventArgs-Klasse leitet eine Methode ein: MemberwiseClone(), die eine flache Kopie des Objekts erstellt.The class System.EventArgs introduces one method: MemberwiseClone(), which creates a shallow copy of the object. Diese Methode muss Reflektion verwenden, um ihre Funktionalität für jede von EventArgs abgeleitete Klasse zu implementieren.That method must use reflection in order to implement its functionality for any class derived from EventArgs. Diese Funktionalität lässt sich in einer bestimmten abgeleiteten Klasse einfacher erstellen.That functionality is easier to create in a specific derived class. Das bedeutet, dass das Ableiten von System.EventArgs eine Einschränkung ist, die Ihre Entwürfe beschränkt, aber keine zusätzlichen Vorteile bietet.That effectively means that deriving from System.EventArgs is a constraint that limits your designs, but does not provide any additional benefit. Tatsächlich können Sie die Definitionen von FileFoundArgs und SearchDirectoryArgs ändern, sodass sie nicht von EventArgs abgeleitet werden.In fact, you can changes the definitions of FileFoundArgs and SearchDirectoryArgs so that they do not derive from EventArgs. Das Programm funktioniert genauso.The program will work exactly the same.

Sie könnten auch SearchDirectoryArgs in eine Struktur ändern, wenn Sie eine weitere Änderung vornehmen:You could also change the SearchDirectoryArgs to a struct, if you also make one more change:

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

Die zusätzliche Änderung besteht darin, den Standardkonstruktor aufzurufen, bevor der Konstruktor eingegeben wird, der alle Felder initialisiert.The additional change is to call the default constructor before entering the constructor that initializes all the fields. Ohne diesen Zusatz würden die Regeln von C# berichten, dass auf die Eigenschaften zugegriffen wird, bevor sie zugewiesen wurden.Without that addition, the rules of C# would report that the properties are being accessed before they have been assigned.

Ändern Sie FileFoundArgs nicht von einer Klasse (Verweistyp) in eine Struktur (Werttyp).You should not change the FileFoundArgs from a class (reference type) to a struct (value type). Das Protokoll zur Behandlung von Abbrüchen erfordert, dass die Ereignisargumente als Verweis übergeben werden.That's because the protocol for handling cancel requires that the event arguments are passed by reference. Wenn Sie diese Änderung vorgenommen hätten, könnte die Dateisuche-Klasse niemals Änderungen beobachten, die von einem der Ereignisabonnenten vorgenommen wurden.If you made the same change, the file search class could never observe any changes made by any of the event subscribers. Für jeden Abonnenten würde eine neue Kopie der Struktur verwendet werden, und diese Kopie würde sich von derjenigen unterscheiden, die vom Dateisuche-Objekt gesehen wird.A new copy of the structure would be used for each subscriber, and that copy would be a different copy than the one seen by the file search object.

Schauen wir uns nun an, wie diese Änderung abwärtskompatibel gemacht werden kann.Next, let's consider how this change can be backwards compatible. Das Entfernen der Einschränkung wirkt sich nicht auf vorhandenen Code aus.The removal of the constraint does not affect any existing code. Alle vorhandenen Ereignisargumenttypen werden immer noch von System.EventArgs abgeleitet.Any existing event argument types do still derive from System.EventArgs. Abwärtskompatibilität ist ein wichtiger Grund, weshalb sie weiterhin von System.EventArgs ableiten.Backwards compatibility is one major reason why they will continue to derive from System.EventArgs. Vorhandene Ereignisabonnenten werden Abonnenten für ein Ereignis, die dem klassische Muster folgen.Any existing event subscribers will be subscribers to an event that followed the classic pattern.

Nach derselben Logik würden alle erstellten Ereignisargumenttypen keine Abonnenten in vorhandenen Codebases haben.Following similar logic, any event argument type created now would not have any subscribers in any existing codebases. Neue Ereignistypen, die nicht von System.EventArgs abgeleitet sind, unterbrechen diese Codebases nicht.New event types that do not derive from System.EventArgs will not break those codebases.

Ereignisse mit asynchronen AbonnentenEvents with Async subscribers

Das letzte, in diesem Artikel behandelte Muster ist die richtige Schreibweise von Ereignisabonnenten, die asynchronen Code aufrufen.You have one final pattern to learn: How to correctly write event subscribers that call async code. Informationen hierzu finden Sie im Artikel zu Async und Await.The challenge is described in the article on async and await. Asynchrone Methoden können einen Rückgabetyp „Void“ haben, aber davon ist dringend abzuraten.Async methods can have a void return type, but that is strongly discouraged. Wenn Ihr Ereignisabonnent eine asynchrone Methode aufruft, haben Sie keine Wahl, außer eine async void-Methode zu erstellen.When your event subscriber code calls an async method, you have no choice but to create an async void method. Sie wird von der Signatur des Ereignishandlers benötigt.The event handler signature requires it.

Sie müssen diesen entgegengesetzten Leitfaden abstimmen.You need to reconcile this opposing guidance. Sie müssen irgendwie eine sichere async void-Methode erstellen.Somehow, you must create a safe async void method. Die Grundlagen des Musters, das Sie implementieren müssen, finden Sie untenan:The basics of the pattern you need to implement are below:

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

Beachten Sie, dass der Handler als ein asynchroner Handler markiert ist.First, notice that the handler is marked as an async handler. Da er einem Ereignishandler-Delegattyp zugewiesen wurde, weist er den Rückgabetyp „Void“ auf.Because it is being assigned to an event handler delegate type, it will have a void return type. Das bedeutet, dass Sie dem im Handler angezeigten Muster folgen müssen und nicht zulassen dürfen, dass Ausnahmen ausgelöst werden, die nicht zum Kontext des asynchronen Handlers gehören.That means you must follow the pattern shown in the handler, and not allow any exceptions to be thrown out of the context of the async handler. Da der Handler keine Aufgabe zurückgibt, gibt es keine Aufgabe, die einen Fehler berichten kann, indem sie in den Fehlerzustand tritt.Because it does not return a task, there is no task that can report the error by entering the faulted state. Da die Methode asynchron ist, kann sie die Ausnahme nicht einfach auslösen.Because the method is async, the method can't simply throw the exception. (Die aufrufende Methode wird weiterhin ausgeführt, weil sie async (asynchron) ist.) Das tatsächliche Laufzeitverhalten wird für unterschiedliche Umgebungen unterschiedlich definiert.(The calling method has continued execution because it is async.) The actual runtime behavior will be defined differently for different environments. Sie kann den Thread oder das Programm beenden oder das Programm in einem unbestimmten Zustand belassen.It may terminate the thread, it may terminate the program, or it may leave the program in an undetermined state. Keine dieser Verhaltensweisen erzielt gute Ergebnisse.None of those are good outcomes.

Deshalb sollten Sie die Await-Anweisung für die asynchrone Aufgabe in Ihrem eigenen try-Block umschließen.That's why you should wrap the await statement for the async Task in your own try block. Wenn sie einen fehlerhaften Vorgang verursacht, können Sie den Fehler protokollieren.If it does cause a faulted task, you can log the error. Wenn es sich um einen Fehler handelt, aus dem sich Ihre Anwendung nicht wiederherstellen lässt, können Sie das Programm schnell und problemlos beenden.If it is an error from which your application cannot recover, you can exit the program quickly and gracefully

Dies sind die wichtigsten Updates für das .NET-Ereignismuster.Those are the major updates to the .NET event pattern. In den Bibliotheken, mit denen Sie arbeiten, Sie zahlreiche Beispiele für die früheren Versionen.You will see many examples of the earlier versions in the libraries you work with. Allerdings sollten Sie auch die neuesten Muster kennenlernen.However, you should understand what the latest patterns are as well.

Der nächste Artikel dieser Reihe hilft Ihnen dabei, zwischen der Verwendung von delegates und events in ihren Entwürfen zu unterscheiden.The next article in this series helps you distinguish between using delegates and events in your designs. Da sich beide Konzepte ähneln, wird Ihnen dieser Artikel helfen, die beste Entscheidung für Ihre Programme zu treffen.They are similar concepts, and that article will help you make the best decision for your programs.

NächsteNext