Cómo convertir resultados de linq a HashSet o HashedSet

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.

Básicamente, buscando la última parte de esto:

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

También podría hacer esto:

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

No creo que haya nada incorporado que haga esto … pero es muy fácil escribir un método de extensión:

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

Tenga en cuenta que realmente desea un método de extensión (o al menos un método genérico de algún tipo) aquí, porque es posible que no pueda express 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 . Confiamos en la inferencia de tipo para que los métodos generics lo hagan por nosotros.

Ahora podría elegir ToSet nombre ToSet y devolver ISet – pero me quedaría con ToHashSet y el tipo concreto. 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.

Simplemente pase su IEnumerable al constructor para HashSet.

 HashSet foo = new HashSet(from x in bar.Items select x); 

Como dijo @Joel, puede pasar su enumerable. Si desea hacer un método de extensión, puede hacer:

 public static HashSet ToHashSet(this IEnumerable items) { return new HashSet(items); } 

Esta funcionalidad se ha agregado como un método de extensión en IEnumerable a .NET Framework 4.7.2 :

  • ToHashSet(IEnumerable)
  • ToHashSet(IEnumerable, IEqualityComparer)

Si necesita acceso de solo lectura al conjunto y la fuente es un parámetro para su método, entonces yo iría con

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

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

Eso es bastante simple 🙂

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

y sí T es el tipo especificado por OP 🙂

Existe un método de extensión IEnumerable en .NET framework y en .NET core para convertir un IEnumerable a HashSet : https://docs.microsoft.com/en-us/dotnet/api/?term=ToHashSet

 public static System.Collections.Generic.HashSet ToHashSet (this System.Collections.Generic.IEnumerable source); 

Parece que todavía no puedo usarlo en las bibliotecas estándar de .NET (al momento de escribir este documento). Entonces uso este método de extensión:

  [Obsolete("In the .NET framework and in NET core this method is available, " + "however can't use it in .NET standard yet. When it's added, please remove this method")] public static HashSet ToHashSet(this IEnumerable source, IEqualityComparer comparer = null) => new HashSet(source, comparer); 

La respuesta de Jon es perfecta. 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 bla = new HashedSet((from b in strings select b).ToArray()); 

o

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

¿O me estoy perdiendo algo más?


Editar: Esto es lo que terminé haciendo:

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

Podrías usar el constructor IEnumerable HashSet.

 HashSet foo = new HashSet((from x in bar.Items select x).ToArray()); 

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

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

pero, mi preferencia sería usar selectores:

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

Hacen lo mismo, y el segundo es obviamente más corto, pero creo que el idioma me queda mejor (creo que es como ToDictionary).

Aquí está el método de extensión para usar, con soporte para los comparadores personalizados como una bonificación.

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