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> 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));
}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)));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();A B C
Merke
Func<..., R>,Action<...>undPredicate<T>decken fast alle Delegate-Fälle ab- Lambdas
(a, b) => a + bfangen Variablen ihrer Umgebung ein (Closure) - Events (
event EventHandler<T>) benachrichtigen Beobachter:+=zum Anmelden,-=zum Abmelden - Funktionen lassen sich in Variablen, Listen und Dictionaries speichern
Aufgabe
Baue eine Klasse Thermostat mit einem Event TemperaturGeaendert, das beim Setzen eines neuen Wertes ausgelöst wird.