1. Einführung
Stell dir vor, du kommst in eine Bibliothek und musst alle Bücher zum Thema Programmierung finden, die nach 2020 erschienen sind, sortiert nach dem Namen des Autors. Wahrscheinlich würdest du nicht von Regal zu Regal rennen und jedes Buch einzeln checken, oder? Du würdest den Bibliothekar fragen, der weiß, wie man sowas schnell findet.
LINQ (Language Integrated Query, also "Abfragen, die in die Sprache integriert sind") – das ist unser "smarter Bibliothekar", ein "SQL-Engine" (also ein Tool zum Schreiben von Datenabfragen) direkt in C#!
Denk an LINQ wie an eine Sprache, mit der du beschreibst, was du aus den Daten haben willst, nicht wie du es bekommst. Anstatt zu sagen: "Nimm das erste Element, prüfe die Bedingung, wenn es passt, füge es einer neuen Liste hinzu, dann geh zum nächsten...", sagst du einfach: "Gib mir alle Produkte, deren Preis über 1000 liegt." Und C# kümmert sich selbst drum, das möglichst effizient zu machen.
Der Kern von LINQ: Einen standardisierten Syntax für Abfragen an alle Datenquellen zu bieten, die das Interface IEnumerable<T> implementieren. Und das ist genial, weil List<T>, Arrays, HashSet<T> – die implementieren alle IEnumerable<T>. Und wenn deine Daten in einer Datenbank liegen, dann wandeln spezielle Libraries (wie Entity Framework) deine LINQ-Queries in echtes SQL um!
LINQ gibt's in C# schon seit über 10 Jahren. Das war ein Meilenstein, der die Arbeit mit Daten in .NET komplett verändert hat. Vor LINQ mussten Entwickler viel Boilerplate-Code schreiben, um Collections zu filtern, zu sortieren oder zu transformieren. Oder sie haben SQL-Strings benutzt, die der Compiler nicht prüft – Fehler gab's dann erst zur Laufzeit. LINQ hat das Konzept der "Abfragen" direkt in die Programmiersprache gebracht, typensicher und viel lesbarer.
Viele sagen, LINQ ist eine der wichtigsten Neuerungen in C# seit der Einführung von Generics.
2. Vorteile von LINQ: Warum lieben es alle?
Kurz und lesbar: Um zum Beispiel Produkte mit Preis über 1000 in einer großen Datenbank zu filtern, musste man früher mehrere Zeilen Code schreiben. Mit LINQ geht das in einer einzigen. Weniger Code – weniger Fehlerquellen, mehr Übersicht und Verständlichkeit. Der Code wird fast wie natürliche Sprache.
Programmiererwitz: "Je weniger Code ich schreibe, desto weniger Bugs kann ich einbauen." LINQ hilft dabei!
Mächtig und flexibel: LINQ bietet einen fetten Satz an Operationen: Filtern (Where), Projektion (Select), Sortieren (OrderBy), Gruppieren (GroupBy), Aggregation (Sum, Average), Joinen (Join) und noch viel mehr. Damit kannst du auch komplexe Datenaufgaben easy lösen, indem du die Operationen kombinierst.
Typensicherheit: Das ist super wichtig! Wenn du eine SQL-Abfrage als String schreibst, weiß der Compiler nix davon. Tippst du dich beim Spaltennamen, merkst du das erst zur Laufzeit, wenn die App crasht. Mit LINQ checkt der C#-Compiler deine Query direkt beim Kompilieren. Versuchst du, ein nicht-existierendes Feld von Product abzufragen, gibt's sofort einen Fehler. Das ist wie ein persönlicher Korrektor, der deine Typos findet, bevor sie jemand anders sieht.
Integration in die Sprache: LINQ-Queries sind keine "magischen" Strings. Das sind ganz normale C#-Konstrukte, die mit bekannten Lambda-Ausdrücken und Datentypen arbeiten. Der Umstieg auf LINQ ist also richtig smooth.
Deferred Execution (Verzögerte Ausführung): Das ist eines der coolsten und vielleicht auch kniffligsten Konzepte von LINQ, aber sehr wichtig. Der Punkt ist: Eine LINQ-Query wird nicht sofort ausgeführt, wenn du sie schreibst. Sie wird "gebaut" und wartet, bis du wirklich das Ergebnis willst (zum Beispiel, wenn du sie mit foreach durchläufst). Das macht Queries effizienter, vor allem bei großen Datenmengen. Mehr dazu gibt's in Vorlesung 166. Für jetzt: LINQ ist smart und macht keine unnötige Arbeit.
Universell und erweiterbar: LINQ ist nicht nur für Listen im Speicher. Es gibt verschiedene LINQ-"Provider":
- LINQ to Objects: für Collections im Speicher (List<T>, Arrays usw.).
- LINQ to SQL / Entity Framework Core: für SQL-Datenbanken (deine LINQ-Queries werden zu SQL-Queries).
- LINQ to XML: für die Arbeit mit XML-Dokumenten.
- Und viele mehr.
Das heißt: Wenn du LINQ drauf hast, kannst du mit Daten aus allen möglichen Quellen arbeiten – mit derselben Query-Logik.
3. Das Problem mit Collections "oldschool"
Vor LINQ sah Code zum Bearbeiten von Collections in C# ungefähr so aus:
var products = new List<Product> { /* ... */ };
var expensive = new List<Product>();
foreach (var prod in products)
{
if (prod.Price > 100)
expensive.Add(prod);
}
expensive.Sort((a, b) => a.Price.CompareTo(b.Price));
foreach (var item in expensive)
Console.WriteLine(item.Name);
So ein Ansatz ist typisch: Erst manuell filtern, dann sortieren – immer einen Zwischenspeicher bauen. Je mehr Logik, desto mehr Code, Fehler und Variablen. Das erinnert an eine Möbelwerkstatt: Die Teile sind da, aber der Zusammenbau ist Handarbeit und anstrengend.
Alice im Schleifenland
Stell dir vor, du hast eine riesige User-Liste und willst nur die Namen derer, die älter als 18 sind und in der Stadt Neonville wohnen, alphabetisch sortiert und die ersten drei ausgeben. Du müsstest verschachtelte Schleifen, Bedingungen, Sortierungen, Hilfslisten schreiben... oder einfach LINQ benutzen.
4. Wie sieht eine LINQ-Query aus? Einfache Beispiele
LINQ bietet zwei Haupt-Syntaxen:
- Method Syntax (Methoden-Chain)
- Query Syntax (SQL-ähnliche Sprache)
Meistens nutzt man Method Syntax, vor allem in modernen Projekten. Hier ein Beispiel aus unserer App:
var expensive = products
.Where(p => p.Price > 100)
.OrderBy(p => p.Price)
.Select(p => p.Name)
.Take(3);
foreach (var name in expensive)
Console.WriteLine(name);
Alles klar: Filter, Sortierung, Auswahl eines Felds, Limit. Minimaler Code – maximaler Sinn.
Das gleiche mit Query Syntax
var expensive = from p in products
where p.Price > 100
orderby p.Price
select p.Name;
foreach (var name in expensive.Take(3))
Console.WriteLine(name);
Beide Varianten liefern das gleiche Ergebnis – nimm die, die dir besser gefällt (aber Method Syntax ist meistens beliebter).
5. LINQ-Architektur: Was steckt dahinter?
LINQ arbeitet mit allen Collections, die das Interface IEnumerable<T> (oder IQueryable<T>, dazu später mehr) implementieren.
Die Hauptkomponenten von LINQ
| Komponente | Kurzbeschreibung |
|---|---|
| LINQ-Extension-Classes | Statische Methoden (, , usw.) in der Klasse |
| Delegates | Meistens und |
| Verzögerte Ausführung | Die Query wird erst beim ersten Durchlauf (z.B. mit ) ausgeführt |
| Query Provider | (für LINQ to SQL, LINQ to Entities) – wandelt die Methoden-Chain in SQL-Queries usw. um |
Visuelles Schema
Collection (List<Product>, T[], ...)
│
▼
LINQ-Methoden (Where, OrderBy, Select...)
│
▼
Query (IEnumerable<T>)
│
▼
Echte Ausführung (foreach, ToList, Count, ...)
6. Beispiel: Schritt-für-Schritt-Analyse einer LINQ-Chain in unserer App
Zurück zu unserer Mini-App mit der Klasse Product:
// Produkt-Klasse
class Product
{
public string Name { get; set; }
public double Price { get; set; }
}
Hier ist eine Produktliste:
var products = new List<Product>
{
new Product { Name = "Käse", Price = 250.5 },
new Product { Name = "Brot", Price = 30 },
new Product { Name = "Milch", Price = 80 },
new Product { Name = "Kaffee", Price = 330 },
new Product { Name = "Butter", Price = 140 }
};
Angenommen, unsere Aufgabe ist: Gib alle Produktnamen aus, die teurer als 100 Euro sind, sortiert nach Preis.
Oldschool-Methode
var filtered = new List<Product>();
foreach (var p in products)
{
if (p.Price > 100)
filtered.Add(p);
}
filtered.Sort((a, b) => a.Price.CompareTo(b.Price));
foreach (var p in filtered)
Console.WriteLine(p.Name);
LINQ-Methode
var expensive = products
.Where(p => p.Price > 100)
.OrderBy(p => p.Price)
.Select(p => p.Name);
foreach (var name in expensive)
Console.WriteLine(name);
In der ersten Zeile beschreibst du direkt die Aufgabe: "Filtere, wo Price > 100, sortiere nach Preis, wähle die Namen".
7. Häufige LINQ-Operationen und ihre "oldschool"-Gegenstücke
| Operation | Oldschool-Logik | LINQ |
|---|---|---|
| Filtern | foreach + if + Add | |
| Projektion (Feld auswählen) | foreach + Add(field) | |
| Sortieren | Sort(comparer) | , |
| Eindeutige Werte | foreach + contains + Add | |
| Zählen | foreach + counter++ | , |
| Bedingung prüfen | foreach + if | , |
| Erstes/letztes Element finden | foreach + if + break | , , |
| Anzahl begrenzen | foreach + Zähler + break | , |
8. Praxis: LINQ ins eigene Projekt einbauen
Nehmen wir den Basis-Code unserer App (Product-Liste) und probieren ein paar nützliche LINQ-Operationen aus.
Filtern und Sortieren
// Wähle Produkte unter 200 Euro und sortiere nach Name
var cheapProducts = products
.Where(p => p.Price < 200)
.OrderBy(p => p.Name);
foreach (var p in cheapProducts)
Console.WriteLine($"{p.Name}: {p.Price} Euro");
Projektion (Transformation) der Collection
// Liste der Preise (double) holen
var prices = products.Select(p => p.Price);
foreach (var price in prices)
Console.WriteLine(price);
Erstes passendes Element finden
// Erstes Produkt, dessen Name mit "K" beginnt
var firstK = products.FirstOrDefault(p => p.Name.StartsWith("K"));
Console.WriteLine(firstK?.Name ?? "Nicht gefunden");
Anzahl der Produkte über 100 Euro zählen
int count = products.Count(p => p.Price > 100);
Console.WriteLine($"So viele Produkte: {count} Stk.");
Ab dieser Vorlesung werden wir LINQ aktiv nutzen, um Collections zu bearbeiten, zu filtern, Daten auszuwählen, zu aggregieren und vieles mehr. Neue Methoden und Features von LINQ (inklusive der Neuheiten aus .NET 9!) schauen wir uns in den nächsten Vorlesungen an. Bis dahin – probier ruhig einfache Queries aus und spür die Power von LINQ!
GO TO FULL VERSION