2010-08-12 15 views
144

Tengo una propiedad en una clase que es un ISet. Estoy tratando de obtener los resultados de una consulta linq en esa propiedad, pero no puedo encontrar la manera de hacerlo.Cómo convertir resultados de linq a HashSet o HashedSet

Básicamente, en busca de la última parte de este:

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

También se podría hacer esto:

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

Editar: Esto es lo que terminé haciendo:

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()); 
} 

Respuesta

247

yo no piensan hay algo construido en el que hace esto ... pero es muy fácil escribir un método de extensión:

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

Tenga en cuenta que usted quiere realmente un método de extensión (o por lo menos un método genérico de alguna forma) aquí, porque es posible que no sea capaz de expresar el tipo de T explícitamente:

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

No puede hacer eso con una llamada explícita al constructor HashSet<T>. Confiamos en la inferencia de tipo para que los métodos genéricos lo hagan por nosotros.

Ahora podría elegir el nombre que ToSet y volver ISet<T> - pero me gustaría seguir con ToHashSet y el tipo de hormigón. Esto es coherente con los operadores LINQ estándar (ToDictionary, ToList) y permite una expansión futura (por ejemplo, ToSortedSet). Es posible que también desee proporcionar una sobrecarga que especifique la comparación para usar.

+2

Buen punto en tipos anónimos. Sin embargo, ¿los tipos anónimos tienen invalidaciones útiles de 'GetHashCode' y' Equals'? Si no, no van a ser muy buenos en un HashSet ... –

+4

@Joel: Sí, lo hacen. De lo contrario, todo tipo de cosas fallarían. Es cierto que solo usan las comparaciones de igualdad predeterminadas para los tipos de componentes, pero eso suele ser suficiente. –

+0

Es bueno saberlo. En ese caso, sí, el método de extensión sería más útil en general que usar el constructor directamente. –

47

Simplemente pase su IEnumerable al constructor para HashSet.

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

¿Cómo vas a hacer frente a una proyección que resulta en un tipo anónimo? –

+5

@Jon, supongo que es YAGNI hasta que se confirme lo contrario. –

+1

Claramente, no. Afortunadamente, en este ejemplo particular, no es necesario. –

1

Eso es bastante simple :)

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

y sí T es el tipo especificado por OP :)

+0

Eso no especifica un argumento de tipo. –

+0

Lol que tiene que ser el voto más duro del día :). Para mí, ahora hay exactamente la misma información. Me gana por qué el sistema de inferencia de tipo aún no está deduciendo ese tipo de todos modos :) –

+0

Deshace ahora ... pero en realidad es bastante significativo precisamente * porque * no se obtiene la inferencia de tipo. Ver mi respuesta –

0

Se podía utilizar el constructor IEnumerable HashSet.

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

El 'ToArray' no logra nada excepto hacer que la computadora trabaje más. –

+10

¡Sin embargo, mantenerlos ocupados es la única manera en que impediremos que se levanten contra nosotros! – Jimmy

16

Como se ha indicado @ Joel, sólo puede pasar su enumerables Si usted quiere hacer un método de extensión, que puede hacer:.

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

Lol, Skeeted nuevamente: P –

+4

+1 b/c te mereces algo por solo estar 2 minutos detrás del Skeet. –

0

respuesta de Jon es perfecto. La única advertencia es que, usando HashedSet de NHibernate, necesito convertir los resultados a una colección. ¿Hay una manera óptima de hacer esto?

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

o

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

O me estoy perdiendo algo más?

+0

'ToList' es generalmente más eficiente que' ToArray'. ¿Solo necesita implementar 'ICollection '? –

+0

Correcto. Terminé yendo con su método, luego lo usé para hacer el ToHashedSet (vea la edición en la pregunta original). Gracias por tu ayuda. – Jamie

0

En lugar de la simple conversión de IEnumerable a HashSet, a menudo es conveniente convertir una propiedad de otro objeto en un HashSet. Se puede escribir esto como:

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

pero, mi preferencia sería el uso de selectores:

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

Ellos hacen lo mismo, y el segundo es obviamente más corto, pero encuentro los ataques modismo mi cerebro mejor (lo considero como ToDictionary).

Este es el método de extensión a usar, con soporte para los comparadores personalizados como una bonificación.

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

Si necesita acceder simplemente de sólo lectura para el conjunto y la fuente es un parámetro a su método, entonces yo iría con

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); 
} 

La razón es que los usuarios pueden llamar a su método con el ISet ya no necesita crear la copia.

Cuestiones relacionadas