Delegaten ermöglichen es, Methoden als Parameter zu übergeben und sind besonders nützlich für Ereignisse, Rückrufe und flexible Methodenaufrufe.
Ein Delegate ist ein Datentyp, der eine Referenz auf Methoden speichern kann, wodurch Methoden wie Parameter behandelt werden können. Dies ermöglicht es, Methoden als Parameter an andere Methoden zu übergeben oder sie in Ereignstrukturen einzusetzen.
Kleines Beispiel der Verwendung eines einfachen Delegates
public delegate int Berechnung(int a, int b);
// - Definition: Ein Delegate wird ähnlich einer Methode deklariert, lediglich ohne Rumpf
static void Main(string[] args){
Berechnung addiere = (a, b) => a + b;
Console.WriteLine("Ergebnis Addition: " + addiere(2, 2));
Berechnung multipliziere = (c, d) => c * d;
Console.WriteLine("Ergebnis Multiplikation: " + multipliziere (2, 2));
Berechnung dividiere = (e, f) => e / f;
Console.WriteLine("Ergebnis Dividition: " + dividiere(2, 2));
Berechnung subtrahiere = (g, h) => g - h;
Console.WriteLine("Ergebnis Subtration: " + subtrahiere(2, 2));
Console.WriteLine("");
Console.WriteLine("Zum Beenden Taste drücken");
var nix = Console.ReadKey();
}
Was macht der obere Code?
Zuerst wird das Delegate mit der Deklaration „public delegate int Berechnung(int a int b);“ definiert. Das Delegate besitzt keinen Rumpfkörper, sondern lediglich die Parameter, die benötigt werden.
Interessant wird es bei der Zeile „Berechnung addiere = (a, b) => a + b;„:
Es wird eine Variable (addiere) des Delegatentyps Berechnung erstellt. Mit dem Lambda-Ausdruck ((a, b) => a + b) wird festgelegt, was mit den Variablen geschehen soll. In diesem kleinen Beispielprojekt wurde die kompakte Schreibweise gewählt. Lambda-Ausdrücke werden uns im Laufe der Zeit immer wieder begegnen. Innerhalb von Lambda-Ausdrücken können unter anderem auch ganze Methoden definiert werden. Dazu jedoch später mehr. Die Variable addiere ist nun eine Variable, die die Methode speichert. Wird also addiere mit den Parametern aufgerufen, wie in diesem Beispiel addiere(2, 2), werden die beiden Zahlen entsprechend dem Lambda-Ausdruck addiert.
In dem oben gegebenen Code verwende ich 1 Delegate um Berechnungsoperationen durchzuführen und schreibe per Lambda Funktion die genauen Rechenoperationen (Beispiele => a+b, =>c*d, etc.).
Multicast Delegates
Bei Multicast-Delegates können Delegates Methoden abonnieren, die nacheinander aufgerufen werden.
Für dieses Beispiel erstellen wir einfache Rechenoperationen (wie im Code 1) als Funktionen:
static int MCastAddiere(int a, int b) {
Console.WriteLine($"Addiere: {a + b}");
return a + b;
}
static int MCastSubtrahiere(int a, int b){
Console.WriteLine($"Subtrahiere: {a - b}");
return a - b;
}
static int MCastMultipliziere(int a, int b){
Console.WriteLine($"Multipliziere: {a * b}");
return a - b;
}
static int MCastDividiere(int a, int b){
Console.WriteLine($"Dividiere: {a / b}");
return a - b;
}
Um in den Beispielcode zu unterscheiden, habe ich an den Funktionen das Präfix „MCast“ verwendet.
Als nächsten wird ein Delegate erstellt, mit den Parametern, welche für die Berechnungen verwendet werden;
public delegate int Rechne(int a, int b);
Nun werden wir das Delegat vom Typ Rechne erstellen und alle Funktionen, welche wir ausführen möchten an das Delegat vom Typ Rechne hängen:
Rechne rechner = mCastAddiere;
rechner += mCastSubtrahiere;
rechner += mCastMultipliziere;
rechner += mCastDividiere;
Mit „+=“ werden Methoden hinzugefügt und mit „-=“ werden Methoden wieder entfernt.
Und nun Ausführen aller 4 Funktionen das Delegat ausführen:
int ergebnis = rechner(2, 2); // Ausführen aller Funktionen mittels Delegate
Console.WriteLine($"Letztes Ergebnis: {ergebnis}");
Console.WriteLine("Zum Beenden Taste drücken.");
Console.ReadKey();
Wie wir hier sehen, können Multicast-Delegates ein äußerst nützliches Werkzeug sein, um insbesondere wiederholende Aufgaben zu erleichtern. Wenn wir jedoch die Zeile „Console.WriteLine($“Letztes Ergebnis: {ergebnis}“);“ genauer betrachten, stellen wir fest, dass nur das letzte Ergebnis zurückgegeben wird. Daher sollten Multicast-Delegates vor allem bei Methoden mit void-Rückgabewerten verwendet werden, da der aufrufende Code lediglich den Wert der letzten Funktion erhält.
Multicast Delegaten mit Rückgabenwerten
Das ist etwas knifflig. Im ersten Multi Delegate-Beispiel sehen wir, dass Ergebnisse mit Ausnahme des letzten Ergebnisses verloren gehen. Dies ist in der Praxis nicht immer zielführend. Um später mit „allen“ Ergebnissen arbeiten zu können, müssen wir etwas Gehirnschmalz einsetzen.
public delegate int Berechnung(int a, int b);
static void Main(string[] args
int addiere(int a, int b) => a + b;
int multipliziere(int a, int b) => a * b;
int dividiere(int a, int b) => a / b;
int subtrahiere(int a, int b) => a - b;
Berechnung rechne = addiere;
rechne += multipliziere;
rechne += dividiere;
rechne += subtrahiere;
int[] ergebnisse = rechne
.GetInvocationList()
.Select(d => ((Berechnung)d)(6, 2))
.ToArray();
foreach(int ergebnis in ergebnisse)
Console.WriteLine ($"Ergebnis: {ergebnis}");
Console.WriteLine("Zum beenden Taste drücken.");
ConsoleKeyInfo nix = Console.ReadKey();
}
Neu ist hier das Array vom Typ int:
int[] ergebnisse = rechne
.GetInvocationList()
.Select(d => ((Berechnung)d)(6, 2))
.ToArray();
GetInvocationList() gibt ein Array aller im Delegate enthaltenen Methoden zurück.
Mit Select(…) rufen wir gezielt jede Funktion auf, die im Delegate referenziert wird, und erhalten das Ergebnis.
Das Array „ergebnisse“ enthält nun alle Rückgabewerte, auf die wir nach Belieben zugreifen und diese entsprechend verwenden können.
So verlieren wir keinen Rückgabewert mehr.
Hier in diesen Beispielen werden nur Funktionen verwendet die Rechnen, natürlich ist es Möglich mehrere Funktionen zu verwenden, die auch unterschiedliche Dinge mit den Parametern machen.
Alle aufgeführten Listenings sind im Beispielprojekt enthalten.
Download zu Grundlagen Delegaten
Pingback: Event (Grundlagen)