Select und SelectMany in C#: Was ist der Unterschied und wie beeinflusst er die Geschwindigkeit?
Wenn Sie jemals LINQ-Abfragen in C# geschrieben haben, sind Sie sicherlich auf zwei ähnliche, aber sehr unterschiedliche Methoden gestoßen: Select und SelectMany. Auf den ersten Blick "wählen" beide etwas aus einer Sammlung aus. Aber der Teufel steckt, wie immer, im Detail. Die falsche Wahl zwischen ihnen kann Ihren schnellen Code in eine langsame Katastrophe verwandeln, während die richtige Wahl ihn elegant und blitzschnell macht.
In diesem Artikel werden wir nicht nur die Theorie behandeln. Wir führen einen echten Leistungstest durch, werfen einen Blick auf die interne Funktionsweise beider Methoden und geben Ihnen einen klaren Algorithmus: Wann Sie Select und wann SelectMany verwenden sollten. Bereit? Los geht's!
Kurze Einführung: Was sind Select und SelectMany?
Zunächst die Grundlagen. Stellen Sie sich vor, Sie haben eine Liste von Studenten (List<Student>), und jeder Student hat eine Liste von Noten (List<int>).
Select ist eine "Eins-zu-eins"-Projektion. Für jedes Element der ursprünglichen Sammlung erhalten Sie genau ein Element als Ausgabe. Wenn Sie 10 Studenten haben, gibt Select eine Sammlung mit 10 Elementen zurück.
- Beispiel: Die Namen aller Studenten abrufen.
- Signatur:
IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
SelectMany ist eine "Eins-zu-viele"-Projektion mit anschließendem "Abflachen" (Flatten). Jedes Element der ursprünglichen Sammlung kann mehrere Elemente als Ausgabe erzeugen, und alle werden zu einer einzigen flachen Liste zusammengefügt.
- Beispiel: Alle Noten aller Studenten abrufen (eine flache Liste von Zahlen).
- Signatur:
IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector)
Der Unterschied scheint offensichtlich. Aber wo liegt der Haken? Der Haken liegt darin, wie diese Methoden unter der Haube arbeiten und wie sie von Anfängern oft verwechselt werden.
Der Hauptunterschied: Die Form der Ausgabedaten
Lassen Sie uns das gleich an einem Codebeispiel verdeutlichen. Hier ist unsere Modellklasse:
public class Student{ public string Name { get; set; } public List<int> Grades { get; set; } = new List<int>();}
// Daten erstellenvar students = new List<Student>{ new Student { Name = "Anna", Grades = new List<int>{ 5, 4, 5 } }, new Student { Name = "Boris", Grades = new List<int>{ 3, 4 } }, new Student { Name = "Vika", Grades = new List<int>{ 5, 5, 5, 4 } }};Beispiel 1: Select verwenden (falsch)
// Wir möchten alle Noten abrufen. Aber Select gibt eine Sammlung von Sammlungen zurück!var allGradesNested = students.Select(s => s.Grades);// Typ: IEnumerable<List<int>> — drei Listen darin!
foreach (var gradeList in allGradesNested){ foreach (var grade in gradeList) { Console.Write(grade + " "); // Wir müssen eine verschachtelte Schleife verwenden }}// Ausgabe: 5 4 5 3 4 5 5 5 4Das funktioniert, ist aber umständlich. Sie erhalten eine verschachtelte Struktur, die Sie zusätzlich abflachen müssen.
Beispiel 2: SelectMany verwenden (richtig)
// SelectMany erledigt die ganze Arbeit für uns: Es nimmt die Sammlungen darin und "flacht" sie abvar allGradesFlat = students.SelectMany(s => s.Grades);// Typ: IEnumerable<int> — eine flache Liste von Zahlen!
foreach (var grade in allGradesFlat){ Console.Write(grade + " "); // Keine verschachtelten Schleifen!}// Ausgabe: 5 4 5 3 4 5 5 5 4Spüren Sie den Unterschied?