CodeGym /Kurse /C# SELF /Lambda-Ausdrücke mit Func<...

Lambda-Ausdrücke mit Func, Action, Predicate

C# SELF
Level 50, Lektion 1
Verfügbar

1. Einführung

Wisst ihr, was interessant ist? Ein Lambda-Ausdruck für sich ist wie ein Rezept ohne Topf. Er beschreibt, was zu tun ist, braucht aber einen "Container", um im Code zu leben. In C# gibt es dafür fertige generische Delegates: Func, Action und Predicate.

Stellt sie euch als fertige Formen für eure Lambdas vor — nehmt die passende Form und gießt eure Logik rein. Keine selbstgebauten Lösungen oder eigene Delegate-Typen, wenn das Nötige schon da ist.

Ein bisschen Geschichte

Früher, wenn man eine Funktion als Parameter übergeben wollte, musste man einen eigenen Delegate-Typ deklarieren. Das war langwierig, nervig und sah so aus:

delegate int Calculate(int x, int y);

Calculate adder = (a, b) => a + b;

Als in C# Func, Action und Predicate auftauchten, konnte man in 90% der Fälle auf manuelle Delegate-Definitionen verzichten. Sieht jetzt viel einfacher und universeller aus:

Func<int, int, int> adder = (a, b) => a + b;

2. Func<T, TResult> — Funktion mit Rückgabewert

Syntax und Zweck

Func ist ein generischer Delegate, der von 0 bis 16 Parameter akzeptiert und einen Wert zurückgibt.

Func<int, int, int> sum = (x, y) => x + y;

Func<typ1, typ2, ..., typN, TResult> — alle Typen außer dem letzten sind Argumenttypen, der letzte ist der Rückgabetyp.

Beispiele

1. Summe von zwei Zahlen

Func<int, int, int> sum = (x, y) => x + y;
Console.WriteLine(sum(3, 5)); // 8

2. Quadrieren einer Zahl

Func<int, int> square = x => x * x;
Console.WriteLine(square(4)); // 16

3. Ohne Parameter

Func<string> greet = () => "Hello, lambda!";
Console.WriteLine(greet());

Visuelle Darstellung

Signatur Beispiel Beschreibung
Func<int, int>
x => x * 2
Nimmt ein int, gibt ein int zurück
Func<int, int, int>
(a, b) => a + b
Zwei int, gibt ein int zurück
Func<string>
() => "hi"
Nimmt nichts, gibt einen String zurück

Wie das in deiner Anwendung aussieht

Angenommen, in unserer Mini-Anwendung (konzeptionelles "Benutzerverzeichnis") haben wir eine Liste von Zahlen — wir wollen verschiedene Operationen darauf anwenden. Zum Beispiel quadrieren oder eine Summe mit einer festen Zahl berechnen:

List<int> numbers = new() { 1, 2, 3, 4, 5 };

Func<int, int> square = x => x * x;
var squares = numbers.Select(square);
Console.WriteLine(string.Join(", ", squares)); // 1, 4, 9, 16, 25

3. Action<T> — Aktion ohne Rückgabe

Action ist der generische Delegate für Methoden, die etwas tun (z. B. auf die Konsole schreiben), aber nichts zurückgeben.

Er kann von 0 bis 16 Parameter annehmen, gibt aber immer void zurück.

Beispiele

1. Auf die Konsole schreiben

Action<string> print = text => Console.WriteLine("Data: " + text);
print("Hello, world!");

2. Aktion ohne Parameter

Action greet = () => Console.WriteLine("Good welcome!");
greet();

3. Aktion mit mehreren Parametern

Action<int, int> showSum = (a, b) => Console.WriteLine($"Sum: {a + b}");
showSum(2, 3); // Sum: 5

Visuelle Darstellung

Signatur Beispiel Beschreibung
Action
() => ...
Keine Parameter, keine Rückgabe
Action<int>
x => ...
Ein Parameter
Action<int, string>
(x, s) => ...
Mehrere Parameter

In unserer Anwendung

Fügen wir dem Benutzerverzeichnis eine Methode hinzu, um alle Namen zu drucken:

List<string> names = new() { "Anna", "Boris", "Vika" };
Action<string> printName = name => Console.WriteLine("User: " + name);

names.ForEach(printName);
// oder so: names.ForEach(name => Console.WriteLine(name));

4. Predicate<T> — Ja oder Nein?

Wenn du eine Funktion brauchst, die für einen Parameter nur true oder false zurückgibt, verwende Predicate<T>. Das ist einfach ein Delegate, der einen Parameter entgegennimmt und ein bool zurückgibt.

Predicate ist quasi die offizielle "wir brauchen eine boolsche Prüfung"-Hülle für Lambdas.

Beispiele

1. Prüfen, ob eine Zahl größer als 5 ist

Predicate<int> isGreaterThanFive = x => x > 5;

Console.WriteLine(isGreaterThanFive(3)); // false
Console.WriteLine(isGreaterThanFive(7)); // true

2. Verwendung mit List<T>.Find

List<int> values = new() { 2, 4, 7, 10 };
int found = values.Find(isGreaterThanFive); // verwendet Predicate<int>
Console.WriteLine(found); // 7

3. Sind alle erwachsen?

List<int> ages = new() { 12, 19, 34 };

bool allAdults = ages.TrueForAll(age => age >= 18);
// TrueForAll erwartet Predicate<int>

Worin unterscheidet es sich von Func<T, bool>?

Tatsächlich sind sie weitgehend austauschbar. Selbst die Microsoft-Dokumentation sagt: "Predicate<T> ist einfach Func<T, bool> für spezielle APIs". Aber Standardbibliotheksmethoden verlangen manchmal genau Predicate.

5. Wie Lambdas in Func, Action, Predicate "passen"

Wenn du ein Lambda schreibst, analysiert C#: "Oh, seine Form passt zum geforderten Delegate — kann eingesetzt werden!"

Func<int, int> f1 = x => x * 2;
Action<string> a1 = text => Console.WriteLine(text);
Predicate<int> p1 = x => x < 10;

Überall Lambdas! Unter der Haube sind das drei verschiedene Delegates mit unterschiedlichen Signaturen.

Anwendung im "echten" Code-Beispiel

List<User> users = new() {
    new User("Anna", 24),
    new User("Boris", 17),
    new User("Vika", 31),
};

// Funktion, die nur erwachsene Nutzer zurückgibt (Predicate<User>)
List<User> adults = users.FindAll(user => user.Age >= 18);
Console.WriteLine("Liste der Erwachsenen: " + string.Join(", ", adults.Select(u => u.Name)));

Und wenn wir die Namen aller Nutzer ausgeben wollen mit Action<User>:

users.ForEach(user => Console.WriteLine(user.Name));

Ihre Namen erhalten (Func<User, string>):

IEnumerable<string> names = users.Select(user => user.Name);

Tabelle zur Übersicht

Delegate Signatur Beispiel-Lambda Wo verwendet
Func<T, U>
T → U
user => user.Name
Select, beliebige Transformationen
Action<T>
T → void
user => Console.WriteLine(...)
ForEach, Aktions-Methoden
Predicate<T>
T → bool
user => user.Age > 18
Find, Exists, Filter

6. Beispiele mit der internen Anwendung: Schritt für Schritt

Erweitern wir unsere kleine Anwendung "Benutzerverzeichnis". Angenommen, wir haben die Klasse User:

public class User
{
    public string Name { get; }
    public int Age { get; }
    public bool IsActive { get; set; }

    public User(string name, int age)
    {
        Name = name;
        Age = age;
        IsActive = true;
    }
}

1. Func<User, bool> — prüfen, ob ein Benutzer volljährig ist

Func<User, bool> isAdult = user => user.Age >= 18;

Verwendung in LINQ:

var adults = users.Where(isAdult);

2. Predicate<User> — aktiven Benutzer finden

Predicate<User> isActive = user => user.IsActive;
User found = users.Find(isActive);

3. Action<User> — Benutzer deaktivieren

Action<User> deactivate = user => user.IsActive = false;
users.ForEach(deactivate);

4. Func<User, string> — kurze Beschreibung holen

Func<User, string> describe = user => $"{user.Name} ({user.Age})";
var descriptions = users.Select(describe);

All diese Lambdas sind wirklich "Code als Daten", die an Methoden übergeben, in Variablen gespeichert und kombiniert werden können.

7. Nicht offensichtliche Feinheiten und typische Fehler

1. Das Lambda muss zur Signatur des Delegates passen. Wenn die Signatur nicht übereinstimmt, gibt es einen Compiler-Fehler.

Func<int, string> wrong = x => x * 2; // Fehler: string erwartet, int geliefert
// Richtig:
Func<int, string> right = x => (x * 2).ToString();

2. Auf void vs return achten. Action gibt keinen Wert zurück — ein Versuch wie Action<int> a = x => x * x; wird nicht funktionieren, weil ein Wert zurückgegeben wird, obwohl keiner erwartet wird.

3. Predicate<T> und Func<T, bool> sind oft austauschbar, aber nicht immer. Manchmal erwarten Collection-Methoden genau Predicate<T>, manchmal Func<T, bool>. Direkte Zuweisung klappt nicht ohne explizite Umhüllung.

Predicate<int> pred = x => x > 0;
Func<int, bool> func = pred; // Fehler
// Aber:
Func<int, bool> func2 = x => x > 0;
Predicate<int> pred2 = new Predicate<int>(func2); // kann über den Konstruktor konvertiert werden
2
Aufgabe
C# SELF, Level 50, Lektion 1
Gesperrt
Verwendung von `Action` zur Verarbeitung einer Liste von Strings
Verwendung von `Action` zur Verarbeitung einer Liste von Strings
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION