Bereits seit .NET Framework 3.5 unterstützt C# das Feature LINQ. Obwohl mit Hilfe von LINQ der Code gegenüber klassischen Schleifen übersichtlicher und lesbarer wird, hat es sich zum Vermeiden von Schleifen noch nicht bei allen Entwicklern durchgesetzt. Oft wird innerhalb einer Schleife gefiltert und geschachtelt, was lange und unübersichtliche Schleifenblöcke zur Folge hat. Das soll mit LINQ ein Ende haben und jedem C#-Entwickler die Möglichkeit geben, Abfragen auf übersichtliche und lesbare Weise zu schreiben.
Language-Integrated Query (LINQ) ist eine Abfragesprache und bietet die Möglichkeit, Abfragen auf eine Menge von Daten innerhalb von C# zu schreiben. Beispielsweise kann mit LINQ geprüft werden, ob sich ein Element in einer Menge befindet, oder es kann nach Eigenschaften der einzelnen Elemente gefiltert werden. Gleichzeitig gibt es die Möglichkeit, mit Hilfe von LINQ Schleifen zu reduzieren und den Code für Entwickler leserlicher zu machen.
Unter dem Aspekt der Übersichtlichkeit und Wartbarkeit gibt es drei große Probleme bei der Verwendung von Schleifen:
Die Absicht einer Schleife ist selten direkt erkennbar.
Schleifen vereinen oft mehr als eine Absicht im Schleifenkonstrukt.
Das gesamte Schleifenkonstrukt wird benötigt und zerstört den Lesefluss.
Listing 1 ist ein einfaches Beispiel und zeigt, dass auch einfache Code-Snippets mit LINQ schneller verstanden werden können.
Listing 1: Filtern nach geraden Zahlen
IEnumerable<int> GetEvenNumbersWithLoop(IList<int> listOfNumbers)
{
foreach (var number in listOfNumbers)
{
if (number % 2 == 0)
{
yield return number;
}
}
}
IEnumerable<int> GetEvenNumbersWithLinq(IList<int> listOfNumbers)
{
bool Even(int x) => x % 2 == 0;
return listOfNumbers.Where(Even);
}
Obwohl es sich um ein einfaches Beispiel handelt, kann der Code mit LINQ direkt verstanden werden, ohne mit Implementierungsdetails überflutet zu werden. Beim Lesen des Codes kann man aufgrund der Benennungen und der Verwendung einer lokalen Funktion direkt verstehen, was passieren soll. In der Praxis, wo die Beispiele schnell komplexer werden (ein Beispiel zeigt Listing 2), kann es einen großen Unterschied machen, ob der Entwickler die Absicht einer Schleife erfassen kann. Wenn der Entwickler eine Schleife debuggen muss, kann es einige Zeit dauern, bis er den Sinn der Schleife erkannt hat und diese als mögliche Fehlerquelle ausschließen kann. Die LINQ-Funktionen unterstützen bei Abfragen das Method Chaining im Sinne eines Fluent API. Dadurch können mehrere Funktionen hintereinander verarbeitet werden und so zum Lesefluss beitragen.
So gut wie jeder Entwickler kennt das Problem: Es gibt eine Menge von Daten, die unter bestimmten Gesichtspunkten gefiltert, verknüpft und umgewandelt werden müssen. In den meisten Fällen wird für solch ein Problem eine foreach-Schleife verwendet. Innerhalb der Schleife wird nach den Bedingungen gefiltert, weitere Daten für das betreffende Element werden abgefragt und zum Schluss die neugewonnen Daten zusammengefasst. Listing 2 zeigt ein Beispiel, wie solch ein Code für eine Bestellverwaltung aussehen könnte. Es sollen alle Kunden, die mehr als zwei Bestellungen aufgegeben und im aktuellen Monat Geburtstag haben, mit ihrer E-Mail und den Bestelldetails angezeigt werden, damit beispielsweise ein Gutschein an sie versendet werden kann.
Listing 2: Beispiel einer foreach-Schleife
var users = userService.GetUsers();
foreach (var user in users)
{
if (user.Birth.Month == DateTime.Now.Month)
{
var orders = orderService.GetOrdersByUserId(user....