我有一个类的属性,它是一个ISet。我想把一个linq查询的结果放到这个属性中,但不知道该如何操作。
基本上,我想要做的就是最后这部分:
ISet<T> foo = new HashedSet<T>();
foo = (from x in bar.Items select x).SOMETHING;
也可以这样做:
HashSet<T> foo = new HashSet<T>();
foo = (from x in bar.Items select x).SOMETHING;
我不认为有任何内置的方法可以做到这一点...但编写扩展方法非常容易:
public static class Extensions
{
public static HashSet<T> ToHashSet<T>(
this IEnumerable<T> source,
IEqualityComparer<T> comparer = null)
{
return new HashSet<T>(source, comparer);
}
}
T
的类型。var query = from i in Enumerable.Range(0, 10)
select new { i, j = i + 1 };
var resultSet = query.ToHashSet();
HashSet<T>
构造函数来实现这一点。我们依靠通用方法的类型推断来为我们完成这个任务。ToSet
并返回ISet<T>
,但我建议使用ToHashSet
和具体类型。这与标准LINQ运算符(ToDictionary
、ToList
)保持一致,并允许未来扩展(例如ToSortedSet
)。您还可以提供一个重载,指定要使用的比较方式。GetHashCode
和Equals
的有用重写?如果没有,它们在HashSet中就不会有太大作用... - Joel MuellerForEach
,但ToHashSet
更有意义——除了正常的“不达到有用性要求”(我不同意,但...)外,我不知道不使用ToHashSet
的任何理由。 - Jon Skeet将您的IEnumerable传递到HashSet的构造函数中即可。
HashSet<T> foo = new HashSet<T>(from x in bar.Items select x);
bar.Items
的每个项目都将是T
。考虑到使其通用非常容易,并且匿名类型在LINQ中经常出现,我认为值得再走一步。 - Jon Skeet这项功能已作为扩展方法添加到IEnumerable<TSource>
上,适用于.NET Framework 4.7.2和.NET Core 2.0。因此,在.NET 5及更高版本中也可用。
正如@Joel所说,你可以直接传递你的可枚举对象。如果你想使用扩展方法,可以这样做:
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> items)
{
return new HashSet<T>(items);
}
在.NET框架和.NET core中,有一个扩展方法可以将IEnumerable
转换为HashSet
:https://learn.microsoft.com/en-us/dotnet/api/?term=ToHashSet
public static System.Collections.Generic.HashSet<TSource> ToHashSet<TSource> (this System.Collections.Generic.IEnumerable<TSource> source);
目前(撰写本文时)似乎无法在.NET Standard库中使用它。因此,我使用了这个扩展方法:
[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<T> ToHashSet<T>(this IEnumerable<T> source, IEqualityComparer<T> comparer = null) => new HashSet<T>(source, comparer);
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);
}
ISet
方法,因此您不需要创建副本。var foo = new HashSet<T>(from x in bar.Items select x);
是的,T是由OP指定的类型 :)
Jon的答案非常完美。唯一的注意点是,使用NHibernate的HashedSet时,我需要将结果转换为一个集合。有没有最优的方法来做到这一点?
ISet<string> bla = new HashedSet<string>((from b in strings select b).ToArray());
ISet<string> bla = new HashedSet<string>((from b in strings select b).ToList());
还是我漏掉了其他什么吗?
编辑:这是我最终采取的做法:
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());
}
ToList
通常比 ToArray
更有效率。它只需要实现 ICollection<T>
接口吗? - Jon Skeet与其简单地将IEnumerable转换为HashSet,通常更方便的是将另一个对象的属性转换为HashSet。您可以这样编写:
var set = myObject.Select(o => o.Name).ToHashSet();
但是,我的偏好是使用选择器:
var set = myObject.ToHashSet(o => o.Name);
它们做同样的事情,第二个显然更短,但我认为这个习语更适合我的大脑(我认为它就像ToDictionary一样)。
这里是要使用的扩展方法,并带有自定义比较器的支持作为额外奖励。
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);
}
HashedSet
部分。因为C#
和LINQ
没有任何叫做HashedSet
的东西,所以这只会让人感到困惑。 - Jogge