Ich habe Probleme, die Unterschiede zwischen ILookup<TKey, TVal>
und IGrouping<TKey, TVal>
zu artikulieren, und bin gespannt, ob ich das jetzt richtig verstehe. LINQ hat das Problem noch verschärft, indem Sequenzen von IGrouping
Elementen erstellt und mir gleichzeitig eine ToLookup
Erweiterungsmethode gegeben wurden. Es fühlte sich so an, als wären sie gleich, bis ich genauer hinschaute.
var q1 =
from n in N
group n by n.MyKey into g
select g;
// q1 is IEnumerable<IGrouping<TKey, TVal>>
Welches ist gleichbedeutend mit:
var q2 = N.GroupBy(n => n.MyKey, n => n);
// q2 is IEnumerable<IGrouping<TKey, TVal>>
Welches sieht viel aus wie:
var q3 = N.ToLookup(n => n.MyKey, n => n);
// q3 is ILookup<TKey, TVal>
Bin ich in den folgenden Analogien richtig?
IGrouping<TKey, TVal>
ist eine einzelne Gruppe (d. H. Eine Tastenfolge), analog zu KeyValuePair<TKey, TVal>
, wobei der Wert tatsächlich eine Folge von Elementen ist (und nicht ein einzelnes Element).IEnumerable<IGrouping<TKey, TVal>>
ist eine Folge von diesen (ähnlich dem, was Sie erhalten, wenn Sie über einen IDictionary<TKey, TVal>
iterieren).ILookup<TKey, TVal>
ähnelt eher einem IDictionary<TKey, TVal>
, bei dem der Wert eine Folge von Elementen istJa, all das ist richtig.
Und ILookup<TKey, TValue>
erweitert auch IEnumerable<IGrouping<TKey, TValue>>
, sodass Sie alle Schlüssel-/Sammlungspaare durchlaufen sowie (oder stattdessen) nur bestimmte Schlüssel nachschlagen können.
Grundsätzlich stelle ich mir ILookup<TKey,TValue>
als IDictionary<TKey, IEnumerable<TValue>>
vor.
Bedenken Sie, dass ToLookup
eine "Jetzt erledigen" -Operation ist (sofortige Ausführung), während GroupBy
zurückgestellt wird. So wie "pull LINQ" funktioniert, muss IGrouping
s beim Abrufen von GroupBy
s ohnehin alle Daten lesen (da Sie die Gruppe nicht auf halbem Weg durchschalten können), während dies in anderen Implementierungen der Fall ist kann möglicherweise ein Streaming-Ergebnis erzeugen. (In Push LINQ ist dies der Fall. Ich würde erwarten, dass LINQ to Events identisch ist.)
Es gibt einen weiteren wichtigen Unterschied zwischen ILookup und IDictionary: Ersteres erzwingt Unveränderlichkeit in dem Sinne, dass es hier keine Methoden zum Ändern der Daten gibt (außer wenn der Consumer eine explizite Umwandlung vornimmt). Im Gegensatz dazu verfügt IDictionary über Methoden wie "Hinzufügen", mit denen die Daten geändert werden können. Aus der Sicht der funktionalen Programmierung und/oder der parallelen Programmierung ist ILookup also besser. (Ich wünschte, es gäbe auch eine Version von ILookup, die einem Schlüssel anstatt einer Gruppe nur einen Wert zuweist.)
(Übrigens sollte darauf hingewiesen werden, dass die Beziehung zwischen IEnumerable und IList der Beziehung zwischen ILookup und IDictionary etwas ähnlich ist - erstere ist unveränderlich, letztere nicht.)
GroupBy
und ToLookUp
haben fast die gleiche FunktionalitätAU&SZLIG;ERthis: Reference
GroupBy: Der GroupBy-Operator gibt Elementgruppen basierend auf einem Schlüsselwert zurück. Jede Gruppe wird durch ein Gruppierungsobjekt dargestellt.
ToLookup: ToLookup ist dasselbe wie GroupBy; Der einzige Unterschied besteht darin, dass die Ausführung von GroupBy zurückgestellt wird, während die Ausführung von ToLookup sofort erfolgt.
Hiermit können Sie den Unterschied mithilfe von Beispielcode beheben. Angenommen, wir haben eine Klasse, die das Modell Person
repräsentiert:
class Personnel
{
public int Id { get; set; }
public string FullName { get; set; }
public int Level { get; set; }
}
danach definieren wir eine Liste von personnels
wie folgt:
var personnels = new List<Personnel>
{
new Personnel { Id = 1, FullName = "P1", Level = 1 },
new Personnel { Id = 2, FullName = "P2", Level = 2 },
new Personnel { Id = 3, FullName = "P3", Level = 1 },
new Personnel { Id = 4, FullName = "P4", Level = 1 },
new Personnel { Id = 5, FullName = "P5", Level =2 },
new Personnel { Id = 6, FullName = "P6", Level = 2 },
new Personnel { Id = 7, FullName = "P7", Level = 2 }
};
Jetzt muss ich die personnels
nach ihrem Level gruppieren. Ich habe hier zwei Ansätze. mit GroupBy
oder ToLookUp
. Wenn ich wie oben angegeben GroupBy
verwende, wird die Ausführung verzögert ausgeführt. Dies bedeutet, dass beim Durchlaufen der Sammlung das nächste Element möglicherweise berechnet wird oder nicht, bis es angefordert wird.
var groups = personnels.GroupBy(p => p.Level);
personnels.RemoveAll(p => p.Level == 1);
foreach (var product in groups)
{
Console.WriteLine(product.Key);
foreach (var item in product)
Console.WriteLine(item.Id + " >>> " + item.FullName + " >>> " + item.Level);
}
Im obigen Code habe ich zuerst personnels
gruppiert, aber bevor ich es iterierte, habe ich einige personnels
entfernt. Da GroupBy
die verzögerte Ausführung verwendet, werden die entfernten Elemente im Endergebnis nicht berücksichtigt, da die Gruppierung hier im Punkt foreach
erfolgt.
Ausgabe:
2
2 >>> P2 >>> 2
5 >>> P5 >>> 2
6 >>> P6 >>> 2
7 >>> P7 >>> 2
Aber wenn ich den obigen Code wie folgt umschreibe: (Beachten Sie, dass der Code mit dem vorherigen Code identisch ist, außer dass GroupBy
durch ToLookUp
ersetzt wird.)
var groups = personnels.ToLookup(p => p.Level);
personnels.RemoveAll(p => p.Level == 1);
foreach (var product in groups)
{
Console.WriteLine(product.Key);
foreach (var item in product)
Console.WriteLine(item.Id + " >>> " + item.FullName + " >>> " + item.Level);
}
Da ToLookUp
die sofortige Ausführung verwendet, bedeutet dies, dass beim Aufrufen der ToLookUp
-Methode ein Ergebnis generiert und eine Gruppe angewendet wird. Wenn ich also vor der Iteration ein Element aus personnels
entferne, wirkt sich dies nicht auf das Endergebnis aus.
Ausgabe:
1
1 >>> P1 >>> 1
3 >>> P3 >>> 1
4 >>> P4 >>> 1
2
2 >>> P2 >>> 2
5 >>> P5 >>> 2
6 >>> P6 >>> 2
7 >>> P7 >>> 2
Hinweis: GroupBy
und ToLookUp
geben auch unterschiedliche Typen zurück.
Möglicherweise verwenden Sie ToDictionary anstelle von ToLookUp. Beachten Sie jedoch Folgendes: ( reference )
Die Verwendung von ToLookup () ist der von ToDictionary () sehr ähnlich. In beiden Fällen können Sie Schlüssel-, Wert- und Vergleichselektoren angeben. Der Hauptunterschied besteht darin, dass ToLookup () die doppelten Schlüssel zulässt (und erwartet), ToDictionary () jedoch nicht