CodeGym /Kurse /C# SELF /Fortgeschrittene Aggregation mit Aggregate

Fortgeschrittene Aggregation mit Aggregate

C# SELF
Level 32, Lektion 4
Verfügbar

1. Einführung

Die guten alten LINQ-Methoden sind wie Mensa-Essen: Du wählst "Summe", "Minimum" oder "Durchschnitt" und gehst weiter. Aber manchmal willst du was Besonderes. Genau da kommt Aggregate ins Spiel – wie ein Chefkoch, der nach deinem Rezept kocht.

Verglichen mit funktionaler Programmierung ist Aggregate wie Reduce (oder fold): Du gehst durch die Collection und faltest sie Schritt für Schritt zu einem Wert zusammen. Wie genau – das entscheidest du. Volle Kontrolle, volle Kreativität.

Aufgaben, die sich mit Aggregate easy lösen lassen:

  • Komplexe Summen berechnen: z.B. das Produkt aller Zahlen, oder die Summe nur der geraden/ungeraden, oder Summen nach einer Spezialregel.
  • Strings zusammenfügen mit eigener Logik (z.B. mit verschiedenen Trennzeichen für gerade und ungerade Indizes).
  • Text-Reports bauen ("Markdown-Listen", HTML, jedes Custom-Format).
  • Collections mit veränderbarem Zustand bauen (z.B. ein Dictionary aus einer Liste von Objekten nach einer Spezialregel für den Key).
  • Jede komplexe Zählung, die nicht in die Liste der Standard-Aggregatfunktionen passt.

2. Signatur und Funktionsweise der Methode Aggregate

Schauen wir mal in die offizielle Microsoft-Doku zu Enumerable.Aggregate:

public static TAccumulate Aggregate<TSource, TAccumulate>(
    this IEnumerable<TSource> source, 
    TAccumulate seed, 
    Func<TAccumulate, TSource, TAccumulate> func
)

Was bedeutet das alles?

  • source — deine Ausgangs-Collection.
  • seed — "Startwert" (kannst du als Start-Akkumulator sehen).
  • func — Funktion, die zwei Argumente nimmt: den aktuellen Wert (acc), das aktuelle Element der Collection, und ein neues akkumuliertes Ergebnis zurückgibt.

Es gibt auch eine einfache Überladung:

public static TSource Aggregate<TSource>(
    this IEnumerable<TSource> source, 
    Func<TSource, TSource, TSource> func
)

Hier gilt das erste Element der Collection als "Startwert", und dann wird func von zweiten bis zum letzten Element aufgerufen.

3. Die einfachsten Beispiele für Aggregate

Kleine Intrige für Studis: Wusstet ihr, dass Aggregate Sum oder Product ersetzen kann?

int[] zahlen = { 2, 3, 4 };

// Summe berechnen
int summe = zahlen.Aggregate((acc, val) => acc + val); // acc = Akkumulator, val = nächstes Element

// Produkt berechnen
int produkt = zahlen.Aggregate((acc, val) => acc * val);

Console.WriteLine(summe);     // 9
Console.WriteLine(produkt); // 24

Sieht aus wie Sum und Multiply, aber wir machen’s selbst! Kleiner Witz: Mit Sum kannst du immer die Summe berechnen, aber mit Aggregate – die Summe, die "Anti-Summe" und sogar die "Summe der Quadratwurzeln".

4. Aggregate für das Zusammenfügen von Strings

Wir kleben alle Strings zu einem zusammen, getrennt durch Kommas (ohne unnötiges Komma am Ende):

string[] wörter = { "C#", "LINQ", "rocks" };
string ergebnis = wörter.Aggregate((acc, wort) => acc + ", " + wort);
// Ergebnis: "C#, LINQ, rocks"

Wenn die Collection leer sein kann, ist ein Startwert (seed) super praktisch:

// Wir starten mit leerem String
string report = wörter.Aggregate(
    "Technologien: ",
    (acc, wort) => acc + wort + "; ",
    acc => acc.TrimEnd(' ', ';') // Am Ende das überflüssige ";" entfernen
);

Console.WriteLine(report); // "Technologien: C#; LINQ; rocks"

Achte auf das dritte Argument – die Ergebnis-Selector-Funktion, die gibt’s bei der Überladung mit seed. Das ist wie das "Dessert" am Ende des Menüs.

5. Aggregate im eigenen Projekt

Erinnern wir uns an unsere Übungs-App, mit der wir schon ein paar Tage rumspielen. Angenommen, wir haben eine Klasse Student:

public class Student
{
    public string Name { get; set; }
    public int Note { get; set; }
}

Liste der Studierenden:

var studenten = new List<Student>
{
    new Student { Name = "Alisa", Note = 5 },
    new Student { Name = "Bob", Note = 4 },
    new Student { Name = "Finn", Note = 3 },
    new Student { Name = "Maria", Note = 5 }
};

Aufgabe: Einen String wie
"Die Besten: Alisa, Maria"
bauen – also alle, die Note == 5 haben.

Wie machen’s Anfänger meistens:

var beste = "";
foreach (var s in studenten)
{
    if (s.Note == 5)
        beste += s.Name + ", ";
}
beste = beste.TrimEnd(',', ' ');
Console.WriteLine("Die Besten: " + beste);

Und jetzt – LINQ-Style mit Aggregate:

var besteStr = studenten
    .Where(s => s.Note == 5)
    .Select(s => s.Name)
    .Aggregate("Die Besten: ", (acc, name) => acc + name + ", ")
    .TrimEnd(',', ' ');

Console.WriteLine(besteStr);

Das Schöne: Minimaler Code, maximale Lesbarkeit. Selbst wenn dein Chef kein LINQ kennt, versteht er, was du willst (oder er schätzt zumindest deinen Einsatz, wenn nicht).

6. Komplexere Szenarien

Eigentlich kann der Akkumulator in Aggregate alles sein: Zahl, String, Dictionary, sogar deine eigene Klasse oder Struktur.

Beispiel: Aus der Liste der Studierenden die Anzahl pro Note zählen:

var notenZähler = studenten.Aggregate(
    new Dictionary<int, int>(),
    (dict, student) => {
        if (dict.ContainsKey(student.Note))
            dict[student.Note]++;
        else
            dict[student.Note] = 1;
        return dict;
    }
);

// Zum Ausgeben:
foreach (var pair in notenZähler)
{
    Console.WriteLine($"Note {pair.Key}: {pair.Value} Studierende");
}

Das ist im Prinzip wie GroupBy, aber von Hand gebaut. Warum nicht einfach GroupBy nehmen? Manchmal brauchst du eine Spezialaggregation, die es bei den Standard-LINQ-Methoden nicht gibt, z.B. nur zählen, wenn der Student nicht Finn heißt, oder einen Report für einen Report bauen.

7. Visualisierung: Wie funktioniert Aggregate (Flussdiagramm)

Angenommen, wir haben ein Array { 2, 4, 3 } und wollen die Summe akkumulieren:


acc: 2 (erstes Element)
      |
      v
val: 4
acc = acc + val = 2 + 4 = 6
      |
      v
val: 3
acc = acc + val = 6 + 3 = 9
      |
      v
[Alle Elemente verarbeitet]
      |
      v
Ergebnis: 9

Ähnliches Diagramm für die Überladung mit seed:


seed: 0
      |
      v
val: 2
acc = 0 + 2 = 2
      |
      v
val: 4
acc = 2 + 4 = 6
      |
      v
val: 3
acc = 6 + 3 = 9
      |
      v
Ergebnis: 9

Vergleich Aggregate und andere Aggregatmethoden

Methode Standardverhalten Flexibilität Für leere Collections Beispiel
Sum
Zahlen summieren Niedrig Gibt 0 zurück
[1,2,3].Sum() = 6
Count
Elemente zählen Niedrig Gibt 0 zurück
[a,b].Count() = 2
Aggregate
Beliebige Zählung Hoch Braucht seed
[1,2,3].Aggregate((a,b)=>a*b)=6
string.Join
Strings zusammenfügen Mittel Für leer = ""
string.Join(", ", arr)

8. Praktische Tipps, typische Fehler und wichtige Punkte

Weil Aggregate so flexibel ist, kann es zu Überraschungen führen, wenn du es falsch einsetzt. Die häufigsten Fehler:

Manchmal vergessen Entwickler das seed (Startwert) und beachten nicht, dass bei einer leeren Collection die Überladung ohne seed eine Exception (InvalidOperationException) wirft. Für leere Collections also immer die Überladung mit seed nutzen:

var summe = new int[0].Aggregate(0, (acc, n) => acc + n); // Funktioniert! Gibt 0 zurück

Wenn du einen String mit Aggregate zusammenbaust, hast du schnell ein überflüssiges Trennzeichen (z.B. "," am Ende). Am besten entfernst du das mit .TrimEnd(',', ' ') – oder nimm string.Join, wenn du einfach nur Strings zusammenkleben willst.

Ein Akkumulator vom veränderbaren Typ (z.B. List oder Dictionary) wird oft in Aggregate genutzt, aber Vorsicht: Wenn du ihn in-place änderst, zeigen alle Referenzen auf dasselbe Objekt. Das kann zu komischen Effekten bei parallelen Operationen führen oder wenn du Kopien erwartest. Im funktionalen Stil solltest du bei jedem Schritt ein neues Objekt zurückgeben, nicht das alte ändern.

Wenn andere deinen Code nutzen, mach Aggregate nicht unnötig kompliziert, nur weil’s cool aussieht: Für Anfänger wirkt das schwerer als ein einfaches foreach oder Standard-Aggregate. Wenn die Aufgabe simpel ist – nimm Sum, Count, Join. Aber wenn du "Text nach Spezialmuster bauen" willst – dann ist Aggregate dein bester Freund!

9. Bezug zur echten Arbeit, Vorstellungsgesprächen und Frameworks

In der Industrie taucht Aggregate oft da auf, wo du ungewöhnliche Zählungen oder Faltungen brauchst, z.B. beim Erstellen von komplexen Reports, Statistiken, Graphen, Hash-Berechnungen, Generieren von Unique IDs oder sogar beim Bauen von UI-Komponenten aus Collections nach Speziallogik.

In Vorstellungsgesprächen kommen LINQ-Fragen fast immer vor wie: "Wie bekommst du die Summe der Elemente eines Arrays?", "Wie machst du aus einer Liste von Strings einen String?" oder "Wie zählst du die Anzahl der einzigartigen Elemente?" – und oft werden Lösungen mit LINQ und auch Aggregate erwartet. Die Fragen können auch kreativ sein: "Kannst du mit LINQ die Summe der Quadrate der geraden Zahlen berechnen und dann einen String zurückgeben, der diesen Prozess beschreibt?"

In vielen bekannten .NET-Bibliotheken und Frameworks wie Entity Framework, Dapper, RavenDB wird Aggregate selten direkt auf der DB-Seite genutzt, aber im Code ist es für Aggregationen im Speicher sehr beliebt.

2
Aufgabe
C# SELF, Level 32, Lektion 4
Gesperrt
Einfachste Aggregation — Produkt eines Arrays
Einfachste Aggregation — Produkt eines Arrays
1
Umfrage/Quiz
Daten gruppieren, Level 32, Lektion 4
Nicht verfügbar
Daten gruppieren
Fortgeschrittenes LINQ: Gruppierung und Aggregatfunktionen
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION