Kursinhalt
Fortgeschrittenes C# mit .NET
Fortgeschrittenes C# mit .NET
Benutzerdefinierte Ereignisse
Im vorherigen Kapitel haben wir gelernt, wie man Methoden an Ereignishandler bindet, aber ist es möglich, unsere eigenen Ereignisse zu erstellen? Ja, tatsächlich ist es auch ziemlich einfach.
Ein Ereignissystem besteht aus zwei Teilen: Der Publisher-Klasse und der Subscriber-Klasse. Es gibt immer eine einzelne Publisher-Klasse für ein Ereignis, jedoch kann es mehrere Subscriber-Klassen geben.
Eine Publisher-Klasse enthält den Ereignishandler-Delegaten für das Ereignis und eine Methode, um diesen Delegaten bei Bedarf aufzurufen.
Eine Klasse wird in diesem Kontext als Subscriber-Klasse bezeichnet, wenn sie eine Methode an den Ereignishandler der Publisher-Klasse abonniert.
Das folgende Diagramm zeigt den Prozessablauf eines Ereignissystems:
Wir werden ein einfaches Ereignissystem ausgehend vom Standardprojekt erstellen. Innerhalb der C#-Datei erstellen wir zuerst eine neue Klasse namens CounterEvent
, die die Publisher-Klasse darstellen wird, und sie wird eine EventHandler-Instanz haben, die wir mit dem bereits vorhandenen EventHandler
-Typ erstellen:
index
public class CounterEvent { public event EventHandler AtTensMultiple; }
Diese Klasse benötigt auch eine Methode, um den Ereignishandler bei Bedarf aufzurufen. Wir können ein Ereignis erstellen, das immer dann ausgelöst wird, wenn die Bedingung count % 10 == 0
erfüllt ist, wobei count
die Anzahl der Male ist, die der Benutzer auf den CounterBtn
geklickt hat. Definieren wir zuerst die Aufrufmethode innerhalb des CounterEvent, wir nennen sie CountCheck
, weil sie im Grunde den Wert von count
jedes Mal überprüft, wenn sie aufgerufen wird, und AtTensMultiple
aufruft, wenn die Bedingung erfüllt ist.
index
public class CounterEvent { public event EventHandler? AtTensMultiple; public void CountCheck(int count) { if(count % 10 == 0) { EventHandler? handler = AtTensMultiple; if(handler) { AtTensMultiple(this, new EventArgs()); } } } }
Hier wird das this
-Argument übergeben, um den sender
darzustellen, und new EventArgs()
stellt eine neue Instanz einer leeren Klasse dar. Wir können Daten in die Methoden übergeben, die sich beim Ereignishandler angemeldet haben, wenn der Ereignishandler aufgerufen wird, indem wir dieses zweite Argument verwenden. Da wir jedoch EventArgs
übergeben, was eine leere Klasse ist, werden keine Daten übergeben. Wir werden gleich sehen, wie man auf diese Weise Daten übergibt.
Jetzt, da die Aufrufmethode definiert ist, ist unsere Publisher-Klasse so gut wie fertig. Wir müssen noch zwei Dinge tun:
- Eine Instanz der Publisher-Klasse erstellen;
- Die Aufrufmethode aufrufen, um sie funktionsfähig zu machen;
Also, zuerst werde ich eine Instanz innerhalb der MainPage-Klasse erstellen:
index
public static CounterEvent counter = new CounterEvent();
Und jetzt rufen wir diese Methode innerhalb von OnCounterClicked
auf, damit sie bei jedem Klick des Benutzers auf den Button eine Überprüfung durchführt.
index
private void OnCounterClicked(object? sender, new EventArgs e) { count++; if(count == 0) CounterBtn.Text = $"Clicked {count} time"; else CounterBtn.Text = $"Clicked {count} times"; counter.CounterCheck(count); }
Jetzt, da unser Ereignis funktionsfähig ist, sind wir bereit, eine Methode an den Ereignishandler zu binden. Ich werde eine neue Methode namens Reformat
erstellen, die einfach den Text (Ten's Multiple)
an das Ende des Button-Texts anhängt, wann immer die Methode aufgerufen wird. Unser vollständiger Code sieht jetzt ungefähr so aus:
index
using System.Runtime.CompilerServices; namespace EventsIntro { public class CounterEvent { public event EventHandler? AtTensMultiple; public void CountCheck(int count) { EventHandler? handler = AtTensMultiple; if (handler != null) { if (count % 10 == 0) { handler(this, new EventArgs()); } } } } public partial class MainPage : ContentPage { int count = 0; CounterEvent counter = new CounterEvent(); public MainPage() { InitializeComponent(); CounterBtn.Clicked += OnCounterClicked; counter.AtTensMultiple += Reformat; } private void Reformat(object? sender, EventArgs e) { CounterBtn.Text += " " + "(Ten's Multiple)"; } private void OnCounterClicked(object? sender, EventArgs e) { count++; if (count == 1) CounterBtn.Text = $"Clicked {count} time"; else CounterBtn.Text = $"Clicked {count} times"; counter.CountCheck(count); } } }
Wenn wir das Programm jetzt ausführen und den Button 10 Mal klicken, erscheint der Text (Ten's Multiple)
im Button-Text. Wenn Sie ihn 20 Mal drücken, passiert dasselbe erneut. Und so weiter.
Um nun Argumente an die Reformat
-Methode zu übergeben, können wir eine neue Klasse namens CustomEventArgs
erstellen, die von EventArgs
abgeleitet ist, und in dieser Klasse einige öffentliche Eigenschaften haben, die die Parameter darstellen können:
index
public class CounterArgs : EventArgs { public string Text { get; } // Here 'Text' can act as a parameter or argument. public CounterArgs(string text) { this.Text = text; } }
Jetzt, da wir eine CustomEvenArgs
-Klasse haben, müssen wir einige Änderungen an der CounterEvent
-Klasse vornehmen:
index
public class CounterEvent { public delegate void CounterEventHandler(object sender, CounterArgs e); public event CounterEventHandler? AtTensMultiple; public void CountCheck(int count) { CounterEventHandler? handler = AtTensMultiple; if (handler != null) { if (count % 10 == 0) { handler(this, new CounterArgs("(something random)")); } } } }
Im obigen Code erstellen wir zuerst eine neue Definition für den EventHandler und nennen ihn CustomEventHandler
, wobei wir den Datentyp des zweiten Parameters auf CustomEventArgs
setzen.
Zweitens übergeben wir new CounterArgs("something random")
als zweites Argument beim Aufrufen des Event-Handlers. Dadurch können wir die Zeichenfolgendaten (something random)
als Argument an die Refactor
-Methode übergeben, auf die wir so zugreifen können:
index
// Modified signature to support 'CounterArgs' instead of 'EventArgs' private void Reformat(object? sender, CounterArgs e) { CounterBtn.Text += " " + e.Text; }
Also, unser endgültiger Code sollte in etwa so aussehen:
index
using System.Runtime.CompilerServices; namespace EventsIntro { public class CounterEvent { public delegate void CustomEventHandler(object? sender, CounterArgs e); public event CustomEventHandler? AtTensMultiple; public void CountCheck(int count) { CustomEventHandler? handler = AtTensMultiple; if (handler != null) { if (count % 10 == 0) { handler(this, new CounterArgs("(something random)")); } } } } public class CounterArgs : EventArgs { public string Text { get; } public CounterArgs(string text) { this.Text = text; } } public partial class MainPage : ContentPage { int count = 0; CounterEvent counter = new CounterEvent(); public MainPage() { InitializeComponent(); CounterBtn.Clicked += OnCounterClicked; counter.AtTensMultiple += Reformat; } private void Reformat(object? sender, CounterArgs e) { CounterBtn.Text += " " + e.Text; } private void OnCounterClicked(object? sender, EventArgs e) { count++; if (count == 1) CounterBtn.Text = $"Clicked {count} time"; else CounterBtn.Text = $"Clicked {count} times"; counter.CountCheck(count); } } }
Wenn Sie das obige Programm ausführen, wird der Text (something random)
an das Ende des Button-Texts angehängt, wann immer der count
ein Vielfaches von zehn ist. Dies zeigt an, dass die Daten erfolgreich in die Abonnentenmethoden übergeben werden.
Wichtige Punkte:
- Die Publisher-Klasse enthält die Definition und eine Instanz des benutzerdefinierten Ereignishandler-Delegaten;
- Die Definition des Ereignishandler-Delegaten kann übersprungen werden, wenn wir den
EventHandler
-Delegatentyp verwenden, um die Instanz zu erstellen; - Es gibt eine Methode in der Publisher-Klasse, die dafür verantwortlich ist, den Delegaten aufzurufen, wann immer es relevant ist;
- Die Delegateninstanz sollte öffentlich sein, um sie für Abonnentenklassen zugänglich zu machen;
- Wir können Argumente in Ereignishandlermethoden übergeben, indem wir unsere eigene Klasse erstellen, die von
EventArgs
abgeleitet ist und Eigenschaften hat, die die Daten oder Argumente darstellen, die wir übergeben möchten;
1. Welches Schlüsselwort verwenden wir, um einen normalen Delegaten in einen Ereignishandler zu konvertieren?
2. Von welcher Klasse müssen wir erben, wenn wir benutzerdefinierte Ereignismethodenargumente erstellen?
Danke für Ihr Feedback!