2010-08-12 4 views
144

Ich habe eine Eigenschaft auf einer Klasse, die ein ISet ist. Ich versuche, die Ergebnisse einer linq-Abfrage in diese Eigenschaft zu bekommen, kann aber nicht herausfinden, wie das geht.Wie Linq Ergebnisse in HashSet oder HashedSet konvertieren

Grundsätzlich sucht für den letzten Teil dieses:

ISet<T> foo = new HashedSet<T>(); 
foo = (from x in bar.Items select x).SOMETHING; 

Könnte auch dies tun:

HashSet<T> foo = new HashSet<T>(); 
foo = (from x in bar.Items select x).SOMETHING; 

Edit: Das ist, was ich tun endete:

public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source) 
{ 
    return new HashSet<T>(source); 
} 

public static HashedSet<T> ToHashedSet<T>(this IEnumerable<T> source) 
{ 
    return new HashedSet<T>(source.ToHashSet()); 
} 

Antwort

247

ich nicht denken es etwas, in dem gebaut ist tut dies ... aber es ist wirklich einfach, eine Erweiterungsmethode zu schreiben:

public static class Extensions 
{ 
    public static HashSet<T> ToHashSet<T>(
     this IEnumerable<T> source, 
     IEqualityComparer<T> comparer = null) 
    { 
     return new HashSet<T>(source, comparer); 
    } 
} 

Beachten Sie, dass Sie wirklich eine Erweiterungsmethode tun wollen (oder zumindest eine generische Methode in irgendeiner Form) hier, weil Sie nicht in der Lage sein, die Art von T explizit zum Ausdruck bringen:

var query = from i in Enumerable.Range(0, 10) 
      select new { i, j = i + 1 }; 
var resultSet = query.ToHashSet(); 

Sie können das nicht mit einem expliziten Aufruf des HashSet<T>-Konstruktors tun. Wir verlassen uns auf Typinferenz für generische Methoden, um es für uns zu tun.

Sie jetzt könnten wählen es ToSet und zurück ISet<T> zu nennen - aber ich würde Stick mit ToHashSet und dem konkreten Typ. Dies steht im Einklang mit den Standard-LINQ-Operatoren (ToDictionary, ToList) und ermöglicht zukünftige Erweiterungen (z. B. ToSortedSet). Sie können auch eine Überladung angeben, die den zu verwendenden Vergleich angibt.

+2

Guter Punkt bei anonymen Typen. Haben anonyme Typen jedoch nützliche Überschreibungen von 'GetHashCode' und' Equals'? Wenn nicht, werden sie in einem HashSet nicht gut sein ... –

+4

@Joel: Ja, das tun sie. Sonst würden alle möglichen Dinge scheitern. Zugegeben, sie verwenden nur die Standard-Gleichheitsvergleiche für die Komponententypen, aber das ist normalerweise gut genug. –

+0

Gut zu wissen. In diesem Fall wäre die Erweiterungsmethode im Allgemeinen nützlicher als die direkte Verwendung des Konstruktors. –

47

Übergeben Sie einfach Ihr IEnumerable in den Konstruktor für HashSet.

HashSet<T> foo = new HashSet<T>(from x in bar.Items select x); 
+2

Wie gehen Sie mit einer Projektion um, die zu einem anonymen Typ führt? –

+5

@ Jon, ich würde annehmen, dass YAGNI ist, bis anders bestätigt. –

+1

Klar bin ich nicht. Glücklicherweise muss ich in diesem speziellen Beispiel nicht. –

1

Das ist ziemlich einfach ist :)

var foo = new HashSet<T>(from x in bar.Items select x); 

und ja T ist die Art von OP angegeben :)

+0

Das gibt kein Typargument an. –

+0

Lol, das muss die härteste Abstimmung des Tages sein :). Für mich gibt es jetzt genau die gleichen Informationen. Beats me, warum das Typ-Inferenz-System sowieso nicht schon diesen Typ ableitet :) –

+0

Undone jetzt ... aber es ist eigentlich ziemlich bedeutsam * weil * du keine Typ-Inferenz bekommst. Siehe meine Antwort. –

0

Sie könnten nur die IEnumerable HashSet Konstruktor verwenden.

HashSet<T> foo = new HashSet<T>((from x in bar.Items select x).ToArray()); 
+4

Das 'ToArray' bringt nichts, außer dass der Computer zusätzliche Arbeit leistet. –

+10

Sie zu beschäftigen ist der einzige Weg, wie wir verhindern können, dass sie sich gegen uns erheben! – Jimmy

16

Wie @ Joel erwähnt, können Sie einfach Ihre enumerable in geben Wenn Sie eine Erweiterungsmethode tun möchten, können Sie tun:.

public static HashSet<T> ToHashSet<T>(this IEnumerable<T> items) 
{ 
    return new HashSet<T>(items); 
} 
+5

Lol, Skeeted wieder: P –

+4

+1 b/c du verdienst etwas für nur 2 Minuten hinter dem Skeet. –

0

Jon Antwort perfekt ist. Der einzige Nachteil ist, dass ich mit NHibernate's HashedSet die Ergebnisse in eine Sammlung konvertieren muss. Gibt es dafür einen optimalen Weg?

ISet<string> bla = new HashedSet<string>((from b in strings select b).ToArray()); 

oder

ISet<string> bla = new HashedSet<string>((from b in strings select b).ToList()); 

Oder bin ich etwas fehlt noch?

+0

'ToList' ist im Allgemeinen effizienter als' ToArray'. Ist es nur notwendig, ICollection 'zu implementieren? –

+0

Korrekt. Am Ende ging ich mit Ihrer Methode und dann mit dem ToHashedSet (siehe Bearbeiten der Originalfrage). Danke für Ihre Hilfe. – Jamie

0

Anstatt der einfachen Konvertierung von IEnumerable in ein HashSet ist es oft praktisch, eine Eigenschaft eines anderen Objekts in ein HashSet zu konvertieren. Man könnte schreiben dies als:

var set = myObject.Select(o => o.Name).ToHashSet(); 

aber wäre meine Präferenz Selektoren zu verwenden:

var set = myObject.ToHashSet(o => o.Name); 

Sie das gleiche tun, und das die zweite ist offensichtlich kürzer, aber ich finde das Idiom fits meine Gehirne besser (ich denke, es ist wie ToDictionary).

Hier ist die Erweiterungsmethode zu verwenden, mit der Unterstützung für benutzerdefinierte Vergleiche als ein Bonus.

public static HashSet<TKey> ToHashSet<TSource, TKey>(
    this IEnumerable<TSource> source, 
    Func<TSource, TKey> selector, 
    IEqualityComparer<TKey> comparer = null) 
{ 
    return new HashSet<TKey>(source.Select(selector), comparer); 
} 
2

Wenn Sie nur Nur-Lese-Zugriff auf den Satz und die Quelle ist ein Parameter für Ihre Methode, dann würde ich gehen mit

public static ISet<T> EnsureSet<T>(this IEnumerable<T> source) 
{ 
    ISet<T> result = source as ISet<T>; 
    if (result != null) 
     return result; 
    return new HashSet<T>(source); 
} 

Der Grund dafür ist, dass die Benutzer Ihre Methode aufrufen kann mit die ISet bereits, so dass Sie die Kopie nicht erstellen müssen.