Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Im vorherigen Artikel wurden die am häufigsten verwendeten Ereignismuster behandelt. .NET Core hat ein entspannteres Muster. In dieser Version weist die EventHandler<TEventArgs>
Definition nicht mehr die Einschränkung auf, die TEventArgs
eine von System.EventArgs
abgeleitete Klasse sein muss.
Dies erhöht die Flexibilität für Sie und ist abwärtskompatibel. Beginnen wir mit der Flexibilität. Die Implementierung für System.EventArgs verwendet eine Methode, die in System.Object definiert ist: MemberwiseClone(). Diese Methode erstellt eine flache Kopie des Objekts. Diese Methode muss Spiegelung verwenden, um ihre Funktionalität für jede von EventArgs
abgeleitete Klasse zu implementieren. Diese Funktionalität ist einfacher in einer bestimmten abgeleiteten Klasse zu erstellen. Das bedeutet effektiv, dass die Ableitung von System.EventArgs eine Einschränkung ist, die Ihre Designs begrenzt, aber keine zusätzlichen Vorteile bietet. Tatsächlich können Sie die Definitionen von FileFoundArgs
und SearchDirectoryArgs
ändern, sodass sie nicht von EventArgs
abgeleitet sind. Das Programm funktioniert genau gleich.
Sie könnten auch SearchDirectoryArgs
in eine Struktur ändern, wenn Sie eine weitere Änderung vornehmen:
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 parameterlosen Konstruktor aufzurufen, bevor der Konstruktor eingegeben wird, der alle Felder initialisiert. Ohne diese Ergänzung würden die Regeln von C# melden, dass auf die Eigenschaften zugegriffen wird, bevor sie zugewiesen werden.
Sie sollten die FileFoundArgs
nicht von einer Klasse (Verweistyp) in eine Struktur (Werttyp) ändern. Das Protokoll zur Behandlung von Abbrüchen verlangt, dass Sie Ereignisargumente als Verweis übergeben. Wenn Sie dieselbe Änderung vorgenommen haben, konnte die Dateisuchklasse niemals Änderungen beobachten, die von einem der Ereignisabonnenten vorgenommen wurden. Eine neue Kopie der Struktur würde für jeden Abonnenten verwendet werden, und diese Kopie wäre eine andere Kopie als die kopie, die vom Dateisuchobjekt angezeigt wird.
Als Nächstes überlegen wir, wie diese Änderung abwärtskompatibel sein kann. Das Entfernen der Einschränkung wirkt sich nicht auf vorhandenen Code aus. Alle vorhandenen Ereignisargumenttypen werden weiterhin von System.EventArgs
abgeleitet. Abwärtskompatibilität ist ein wichtiger Grund, warum sie weiterhin von System.EventArgs
abgeleitet werden. Alle vorhandenen Ereignisabonnenten sind Abonnenten eines Ereignisses, das dem klassischen Muster folgt.
Nach ähnlicher Logik verfügt jeder ereignisargumenttyp, der jetzt erstellt wurde, über keine Abonnenten in vorhandenen Codebasen. Neue Ereignistypen, die nicht von System.EventArgs
abgeleitet werden, unterbrechen diese Codebasen nicht.
Ereignisse mit Async-Abonnenten
Das letzte, in diesem Artikel behandelte Muster ist die richtige Schreibweise von Ereignisabonnenten, die asynchronen Code aufrufen. Informationen hierzu finden Sie im Artikel zu Async und Await. Asynchrone Methoden können einen void-Rückgabetyp haben, davon wird jedoch abgeraten. Wenn Ihr Ereignisabonnentcode eine asynchrone Methode aufruft, haben Sie keine Wahl, sondern eine async void
-Methode zu erstellen. Für die Ereignishandlersignatur ist dies erforderlich.
Sie müssen diese entgegengesetzte Anleitung in Einklang bringen. Sie müssen irgendwie eine sichere async void
-Methode erstellen. Die Grundlagen des Musters, das Sie implementieren müssen, sind im folgenden Code dargestellt:
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 zunächst, dass der Handler als asynchroner Handler markiert ist. Da er einem Ereignishandler-Delegattyp zugewiesen wurde, weist er den Rückgabetyp „void“ auf. Dies bedeutet, dass Sie dem Muster folgen müssen, das im Handler angezeigt wird, und nicht zulassen, dass Ausnahmen außerhalb des Kontexts des asynchronen Handlers geworfen werden. Da keine Aufgabe zurückgegeben wird, gibt es keine Aufgabe, die den Fehler melden kann, indem der fehlerhafte Zustand eingegeben wird. Da die Methode asynchron ist, kann die Methode die Ausnahme nicht auslösen. (Die aufrufende Methode setzt die Ausführung fort, da sie async
ist.) Das tatsächliche Laufzeitverhalten wird für verschiedene Umgebungen unterschiedlich definiert. Er kann den Thread oder den Prozess beenden, der den Thread besitzt, oder den Prozess in einem unbestimmten Zustand belassen. All diese potenziellen Ergebnisse sind sehr unerwünscht.
Sie sollten den await
-Ausdruck für die asynchrone Aufgabe in Ihrem eigenen try-Block umschließen. Falls eine Aufgabe fehlerhaft wird, können Sie den Fehler protokollieren. Wenn es sich um einen Fehler handelt, von dem Ihre Anwendung nicht wiederhergestellt werden kann, können Sie das Programm schnell und ordnungsgemäß beenden.
In diesem Artikel wurden die wichtigsten Updates für das .NET-Ereignismuster erläutert. Möglicherweise werden viele Beispiele für die früheren Versionen in den Bibliotheken angezeigt, mit denen Sie arbeiten. Sie sollten jedoch auch verstehen, was die neuesten Muster sind. Den fertigen Code für das Beispiel finden Sie unter Program.cs.
Der nächste Artikel dieser Reihe hilft Ihnen, zwischen der Verwendung von delegates
und events
in Ihren Designs zu unterscheiden. Sie sind ähnliche Konzepte, und dieser Artikel hilft Ihnen, die beste Entscheidung für Ihre Programme zu treffen.