EMZETT.
Login

Kurz: Ein Delegate ist ein Typ für Methoden. Wichtigste fertige Delegates: Action (ohne Rückgabe), Func<…> (mit Rückgabe), Predicate<T> (liefert bool).

Teil des Kurses C#

Kapitel 12 von 16 im Kurs C#. Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Delegates und Lambdas

Ein Delegate ist ein Typ für Methoden. Wichtigste fertige Delegates: Action (ohne Rückgabe), Func<...> (mit Rückgabe), Predicate<T> (liefert bool).

Func<int, int> quadrat = x => x * x;
Func<int, int, int> plus = (a, b) => a + b;
Action<string> ausgabe = s => Console.WriteLine("> " + s);
Predicate<string> leer = string.IsNullOrEmpty;
 
ausgabe($"{quadrat(7)} {plus(3, 4)} {leer("")}");
 
int faktor = 10;
Func<int, int> mal = x => x * faktor;                     // Closure: fängt faktor ein
faktor = 3;
Console.WriteLine(mal(5));
 
Func<int, Func<int, int>> addierer = a => b => a + b;     // Funktion, die eine Funktion liefert
var plus5 = addierer(5);
Console.WriteLine(plus5(10));
 
var woerter = new List<string> { "Birne", "Apfel", "Kiwi" };
woerter.Sort((a, b) => a.Length.CompareTo(b.Length));
woerter.ForEach(w => Console.Write(w + " "));
Console.WriteLine();
 
static int Rechne(Func<int, int, int> op, int a, int b) => op(a, b);
Console.WriteLine($"{Rechne((a, b) => a * b, 6, 7)} {Rechne(Math.Max, 3, 9)}");        // Methodengruppe als Delegate

Ausgabe:

> 49 7 True
15
15
Kiwi Apfel Birne
42 9

Lambdas fangen Variablen per Referenz ein: Änderungen von außen wirken sich aus (mal(5) ergibt 15, nicht 50).

Events

Ein Event erlaubt einer Klasse, andere über etwas zu informieren (Beobachter-Muster):

var knopf = new Knopf();
knopf.Geklickt += (sender, e) => Console.WriteLine($"Handler 1: Klick Nr. {e.Nummer}");
knopf.Geklickt += (sender, e) => Console.WriteLine($"Handler 2: {e.Nummer * 10}");
knopf.Klicken();
knopf.Klicken();
 
class KlickArgs(int nummer) : EventArgs
{
    public int Nummer { get; } = nummer;
}
 
class Knopf
{
    public event EventHandler<KlickArgs>? Geklickt;                  // nur die Klasse selbst löst aus
    private int _zaehler;
    public void Klicken() => Geklickt?.Invoke(this, new KlickArgs(++_zaehler));
}

Ausgabe:

Handler 1: Klick Nr. 1
Handler 2: 10
Handler 1: Klick Nr. 2
Handler 2: 20

Außen kann man sich nur an- und abmelden (+=, -=); nur die Klasse löst das Event aus. ?.Invoke verhindert einen Fehler, falls niemand zuhört. Vergiss das Abmelden (-=) nicht, sonst hält der Sender den Empfänger am Leben (Speicherleck).

Funktionen als Daten

var befehle = new Dictionary<string, Func<double, double, double>>
{
    ["+"] = (a, b) => a + b,
    ["-"] = (a, b) => a - b,
    ["*"] = (a, b) => a * b,
    ["/"] = (a, b) => b == 0 ? double.NaN : a / b,
};
foreach (var (zeichen, op) in befehle) Console.Write($"8 {zeichen} 2 = {op(8, 2)}   ");
Console.WriteLine();
 
Func<int, bool>[] regeln = [x => x > 0, x => x % 2 == 0, x => x < 100];
Console.WriteLine(regeln.All(r => r(42)) + " " + regeln.All(r => r(101)));

Ausgabe:

8 + 2 = 10   8 - 2 = 6   8 * 2 = 16   8 / 2 = 4
True False

Mehrere Delegates verketten

Action a = () => Console.Write("A ");
a += () => Console.Write("B ");
a += () => Console.Write("C ");
a();
Console.WriteLine();

Ausgabe:

A B C

Merke

  • Func<..., R>, Action<...> und Predicate<T> decken fast alle Delegate-Fälle ab
  • Lambdas (a, b) => a + b fangen Variablen ihrer Umgebung ein (Closure)
  • Events (event EventHandler<T>) benachrichtigen Beobachter: += zum Anmelden, -= zum Abmelden
  • Funktionen lassen sich in Variablen, Listen und Dictionaries speichern

Übungsaufgabe

Baue eine Klasse Thermostat mit einem Event TemperaturGeaendert, das beim Setzen eines neuen Wertes ausgelöst wird.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Generics und LINQ

Weiter: Asynchrone Programmierung mit async/await

Alle Kapitel: C# im Überblick