Problem: Distinct() kann nicht nach Feld filtern
\n\nStellen Sie sich vor: Sie haben eine Liste von Kunden, und darin haben sich Duplikate per E-Mail eingeschlichen. Sie schreiben:
\n\nvar uniqueClients = clients.Distinct().ToList();\n\nUnd stellen überrascht fest, dass die Duplikate nicht verschwunden sind. Warum? Weil der Standard-Distinct() ganze Objekte per Referenz (oder per Equals, falls Sie es überschrieben haben) vergleicht. Er weiß nicht, dass für Sie „Eindeutigkeit“ eine eindeutige Email ist, und nicht eine eindeutige Klasseninstanz.
Diese Frage ist eine der beliebtesten auf Stack Overflow zu LINQ (über 500.000 Aufrufe). Und heute schauen wir uns 3 funktionierende Methoden an, um dieses Problem in C# zu lösen. Los geht's!
\n\nMethode 1: GroupBy + First (der Klassiker)
\n\nDie einfachste und verständlichste Methode, die keine Installation von Drittanbieter-Bibliotheken erfordert. Die Idee: Objekte nach der gewünschten Eigenschaft gruppieren und dann das erste Element aus jeder Gruppe nehmen.
\n\nvar uniqueByEmail = clients\n .GroupBy(c => c.Email)\n .Select(g => g.First())\n .ToList();\n\nSo funktioniert es:
\n- \n
GroupBy(c => c.Email)— teilt die Sammlung in Gruppen auf, wobei der Schlüssel die E-Mail ist. \n Select(g => g.First())— nimmt das erste Objekt aus jeder Gruppe. \n - Die restlichen Duplikate werden verworfen. \n
Vorteile:
\n- \n
- Funktioniert „out of the box“ (nur
using System.Linq;). \n - Selbst für Anfänger intuitiv verständlich. \n
- Behält die ursprüngliche Reihenfolge der Elemente bei (das zuerst gefundene Objekt bleibt erhalten). \n
Nachteile:
\n- \n
- Bei großen Datenmengen etwas langsamer (Gruppen müssen erstellt werden). \n
- Der Code ist etwas ausführlicher als gewünscht. \n
Methode 2: MoreLinq und DistinctBy (Eleganz)
\n\nWenn Sie keine Angst vor Drittanbieter-Bibliotheken haben, bietet MoreLinq die ideale Methode DistinctBy. Sie macht genau das, was Sie von Distinct erwarten – filtert nach einer Eigenschaft.
Installation über NuGet:
\nInstall-Package MoreLinq\n\nVerwendung:
\nusing MoreLinq;\n\nvar uniqueByEmail = clients.DistinctBy(c => c.Email).ToList();\n\nEine Zeile – und alles ist erledigt. Intern verwendet DistinctBy ein HashSet und arbeitet in O(n).
Vorteile:
\n- \n
- Äußerst prägnanter und lesbarer Code. \n
- Hohe Leistung. \n
- Die Bibliothek ist gut getestet (über 10 Millionen Downloads). \n
Nachteile:
\n- \n
- Es muss eine Abhängigkeit zum Projekt hinzugefügt werden. \n
- Nicht alle Teams erlauben Drittanbieter-Bibliotheken. \n
Methode 3: Eigener IEqualityComparer (maximale Kontrolle)
\n\nDiese Methode eignet sich, wenn Sie keine Bibliothek einbinden möchten, aber die Vergleichslogik an mehreren Stellen wiederverwenden wollen. Wir erstellen unseren eigenen Comparer:
\n\npublic class ClientEmailComparer : IEqualityComparer<Client>\n{\n public bool Equals(Client x, Client y)\n {\n if (ReferenceEquals(x, y)) return true;\n if (x is null || y is null) return false;\n return x.Email == y.Email;\n }\n\n public int GetHashCode(Client obj)\n {\n return obj.Email?.GetHashCode() ?? 0;\n }\n}\n\nVerwendung:
\nvar comparer = new ClientEmailComparer();\nvar uniqueByEmail = clients.Distinct(comparer).ToList();\n\nVorteile:
\n- \n
- Volle Kontrolle über die Vergleichslogik. \n
- Der Comparer kann in
Contains,Except,Unionusw. wiederverwendet werden. \n - Erfordert keine externen Bibliotheken. \n
Nachteile:
\n- \n
- Viel Boilerplate-Code. \n