CodeGym /Kurse /C# SELF /Weitergabe von Lambda-Ausdrücken ( =>) als Parameter

Weitergabe von Lambda-Ausdrücken ( =>) als Parameter

C# SELF
Level 50, Lektion 2
Verfügbar

1. Einführung

Bis jetzt hast du gesehen: wir haben Methoden von LINQ aufgerufen, die eine Lambda als Eingabe akzeptieren. Aber wie weiß eine C#-Methode überhaupt, dass man ihr eine Funktion übergeben kann?

Kurz gesagt: Delegates sind wie ein Interface für Funktionen: wir beschreiben die Signatur (Parametertypen und Rückgabewert), und jede Methode (und jede Lambda!), die zur Signatur passt, kann dorthin übergeben werden, wo so ein Delegate erwartet wird.

Erinnere dich, wie du einen string als Parameter übergeben hast? So ähnlich funktioniert das mit "Logik", nur ist der Parametertyp ein Delegate.

Delegates: Basistheorie in verständlicher Form

In C# ist ein Delegate ein Typ, der eine "Funktion mit dieser Signatur" beschreibt.

// Delegate, das int nimmt und bool zurückgibt
public delegate bool IntPredicate(int x);

Jede Funktion, die mit der Signatur kompatibel ist, kann einer Variable dieses Typs zugewiesen werden:

bool IsEven(int n) => n % 2 == 0;

IntPredicate pred = IsEven;

Und jetzt passt auch eine Lambda:

IntPredicate pred = x => x % 2 == 0;

Generische Delegates: Func, Action, Predicate

  • Func<T1, ..., TResult> — eine Funktion, die Parameter T1, ... nimmt und TResult zurückgibt.
  • Action<T1, ...> — eine Funktion, die Parameter nimmt und keinen Wert zurückgibt (void).
  • Predicate<T> — eine Funktion, die T nimmt und bool zurückgibt.

2. Lambda an deine Methode übergeben

Stell dir vor, wir entwickeln unsere kleine Lern-Anwendung weiter — ein Konsolenprojekt, das mit einer Liste von Benutzern arbeitet. Früher haben wir Collections über LINQ gefiltert, jetzt schreiben wir unsere eigene Methode, die eine Lambda-Bedingung annimmt.

Erstellen einer Methode mit Lambda-Parameter

// Definieren wir die Klasse User als Beispiel (füge sie in unsere Anwendung ein)
public class User
{
    public string Name { get; set; }
    public bool IsActive { get; set; }
}

// Methode, die eine Liste und ein Delegate-Bedingung (Lambda) annimmt
public static List<User> FilterUsers(List<User> users, Predicate<User> predicate)
{
    var result = new List<User>();
    foreach (var user in users)
    {
        if (predicate(user)) // Wir rufen die Lambda auf!
            result.Add(user);
    }
    return result;
}

Jetzt kann man jede Lambda übergeben:

var users = new List<User>
{
    new User { Name = "Alice", IsActive = true },
    new User { Name = "Bob", IsActive = false },
    new User { Name = "Charlie", IsActive = true }
};

// Filtern nur aktive
var activeUsers = FilterUsers(users, user => user.IsActive);

foreach (var user in activeUsers)
    Console.WriteLine(user.Name); // Alice, Charlie

Das war's! Wir haben ein Stück Logik — eine Mini-Funktion — wie einen normalen Parameter übergeben, einfach weil die Methode FilterUsers ein Predicate<User> erwartet und wir ihr eine passende Lambda gegeben haben.

Variante mit Func<T, TResult>

Predicate<T> passt, wenn du eine Bedingung brauchst (Rückgabe bool). Wenn du aber etwas "berechnen" möchtest für jeden User?

// Methode, die eine Funktion auf jedes Element anwendet und Ergebnisse sammelt
public static List<TResult> MapUsers<TResult>(List<User> users, Func<User, TResult> selector)
{
    var result = new List<TResult>();
    foreach (var user in users)
    {
        result.Add(selector(user));
    }
    return result;
}

Verwendung:

var names = MapUsers(users, user => user.Name.ToUpper());

foreach (var name in names)
    Console.WriteLine(name); // VASYA, PETYA, MASHA

3. Nützliche Feinheiten

Verschiedene Formen der Übergabe

Du kannst nicht nur eine Lambda übergeben, sondern auch eine normale Methode — wichtig ist, dass die Signatur passt.

// Normale Methode
static bool NameHasS(User user) => user.Name.Contains("s");

// Übergabe einer normalen Methode:
var usersWithS = FilterUsers(users, NameHasS);
// Übergabe einer Lambda
var usersWithA = FilterUsers(users, u => u.Name.Contains("a"));

Oder ein anonymes altes Delegate (aber bitte nicht):

var usersWithM = FilterUsers(users, delegate(User u) { return u.Name.Contains("m"); });

Moderner Stil — Lambdas!

Lambdas an LINQ übergeben: was wirklich passiert

var result = users.Where(u => u.IsActive).ToList();

Unter der Haube nimmt Where ein Func<User, bool>. Das bedeutet, jede Methode, die ein Func<...> erwartet, kann genauso verwendet werden!

Und wenn man zwei Parameter will?

// Methode nimmt zwei Lambdas zur Filterung
public static List<User> FilterUsersCustom(
    List<User> users,
    Func<User, bool> include,
    Func<User, bool> exclude)
{
    var result = new List<User>();
    foreach (var user in users)
    {
        if (include(user) && !exclude(user))
            result.Add(user);
    }
    return result;
}

Verwendung:

var customFiltered = FilterUsersCustom(
    users,
    u => u.Name.StartsWith("V"),
    u => u.IsActive == false
);
// Nimmt nur Benutzer, deren Namen mit "V" anfangen und die aktiv sind

Szenario: Filter-Fabrik

Console.WriteLine("Gib die minimale Namenslänge ein:");
int minLength = int.Parse(Console.ReadLine());

Predicate<User> lengthFilter = user => user.Name.Length >= minLength;
var filteredUsers = FilterUsers(users, lengthFilter);

// Ziemlich interaktiv und lebendig!

4. Typische Fehler und Feinheiten

Manchmal kann der Compiler die Typen der Lambda-Parameter nicht "ableiten" — besonders bei komplexen Szenarien mit Overloads oder wenn die Methode einen Delegate mit mehreren Parametern/konkretem Rückgabetyp verlangt. Dann kann man die Typen in der Lambda explizit angeben:

FilterUsers(users, (User u) => u.Name.Length > 3);

oder sogar:

MapUsers(users, (User u) => u.Name.ToUpper());

Fehler: Lambda passt nicht zur Signatur

FilterUsers(users, user => Console.WriteLine(user.Name)); // Fehler! Erwartet bool, erhalten void

Weil erwartet wird, dass die Funktion bool zurückgibt, die Lambda aber void zurückgibt (genauer: nichts explizit zurückgibt). Achte also auf den Rückgabetyp!

Fehler: Übermäßiger Gebrauch von Lambdas

Wenn du anfängst, Lambdas mit 10 Zeilen zu übergeben, pack sie lieber in eine eigene Methode. Das ist lesbarer und Debugging wird einfacher.

2
Aufgabe
C# SELF, Level 50, Lektion 2
Gesperrt
Komplexe Filterung und Umwandlung von Benutzern
Komplexe Filterung und Umwandlung von Benutzern
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION