Einleitung: Die bekannte Angst jedes C#-Entwicklers
\nStell dir vor: Du schreibst eine Abfrage für eine Datenbank oder eine In-Memory-Collection. Du brauchst das erste Element, das eine Bedingung erfüllt. Du bist sicher, dass dieses Element garantiert existiert. Oder doch nicht? Genau hier beginnt die Qual der Wahl: .First() oder .FirstOrDefault()?
Diese Frage ist ein absoluter Dauerbrenner auf Stack Overflow mit tausenden Aufrufen. Und das zu Recht. Die Wahl zwischen diesen beiden Methoden kann den Unterschied zwischen sauberem, vorhersagbarem Code und einem plötzlichen InvalidOperationException in der Produktion bedeuten. Lass uns ein für alle Mal klären, wann welche Methode verwendet wird, und die Zweifel für immer vergessen.
Das Wichtigste in Kürze: Wo liegt der Unterschied?
\nBeide Methoden tun fast dasselbe: Sie geben das erste Element einer Sequenz zurück, das eine Bedingung erfüllt (oder einfach das erste Element, wenn keine Bedingung angegeben ist). Es gibt jedoch einen entscheidenden Unterschied:
\n- \n
- .First() – Wenn kein Element gefunden wird, wird eine
System.InvalidOperationExceptionausgelöst. \n - .FirstOrDefault() – Wenn kein Element gefunden wird, wird der Standardwert des Typs zurückgegeben (
nullfür Referenztypen,0fürint,falsefürboolusw.). \n
Klingt einfach. Aber der Teufel steckt, wie immer, im Detail.
\n\nWann .First() verwenden: Wenn es "sein muss, Punkt"
\nVerwende .First(), wenn deine Geschäftslogik streng garantiert, dass das Element existiert. Wenn es nicht da ist, handelt es sich um eine Ausnahmesituation, einen Fehler in den Daten oder der Anwendungslogik.
Beispiel 1: Suche nach einer eindeutigen ID
\nWenn du einen Benutzer anhand seines Primary Keys in der Datenbank suchst (z. B. nach Id) und sicher bist, dass dieser Benutzer existiert, verwende .First():
public class UserService\n{\n private readonly AppDbContext _context;\n\n public UserService(AppDbContext context)\n {\n _context = context;\n }\n\n public User GetUserById(int userId)\n {\n // Wir erwarten, dass ein Benutzer mit dieser ID existiert.\n // Wenn nicht – das ist ein ernstes Problem, besser eine Exception werfen.\n return _context.Users.First(u => u.Id == userId);\n }\n}\nDie Exception ist in diesem Fall ein Alarmzeichen. Sie sagt: "Hey, die Daten sind beschädigt, oder jemand hat eine falsche ID übergeben". Du fängst diese Exception weiter oben im Stack und behandelst sie als kritischen Fehler.
\n\nBeispiel 2: Erstes Element nach einer Sortierung (garantiert)
\nWenn du eine Liste sortierst und das erste Element nimmst, und die Liste definitiv Elemente enthält:
\nvar highestScore = players\n .OrderByDescending(p => p.Score)\n .First(); // Es kann keine 0 Spieler geben, das haben wir vorher geprüft\nWenn du vorher geprüft hast, dass die Collection nicht leer ist (z. B. if (players.Any())), dann ist .First() deine Wahl.
Wann .FirstOrDefault() verwenden: Wenn das Element fehlen darf
\nDie goldene Regel: Wenn das Fehlen eines Ergebnisses ein normaler Zustand und kein Fehler ist, verwende .FirstOrDefault().
Beispiel 1: Optionale Suche
\nStell dir vor, wir suchen eine Bestellung anhand der Nummer, aber die Bestellung könnte noch nicht angelegt sein:
\npublic Order FindOrderByNumber(string orderNumber)\n{\n // Die Bestellung könnte nicht existieren. Das ist normal.\n return _context.Orders\n .FirstOrDefault(o => o.OrderNumber == orderNumber);\n // Gibt null zurück, wenn die Bestellung nicht gefunden wird.\n}\nNach dem Aufruf dieser Methode müssen wir das Ergebnis auf null prüfen:
var order = FindOrderByNumber("ORD-12345");\nif (order != null)\n{\n // Arbeiten mit der Bestellung\n Console.WriteLine($"Bestellung gefunden: {order.Id}");\n}\nelse\n{\n Console.WriteLine("Bestellung nicht gefunden. Lege neue an.");