[英].net6 & MoreLinq : Call is ambiguous between System.Linq.Enumerable.DistinctBy and MoreLinq.MoreEnumerable.DistinctBy
[英]What is the difference between MoreLINQ's DistinctBy and Linq's GroupBy
我有两个版本的按项目列表分组
List<m_addtlallowsetup> xlist_distincted = xlist_addtlallowsetups.DistinctBy(p => new { p.setupcode, p.allowcode }).OrderBy(y => y.setupcode).ThenBy(z => z.allowcode).ToList();
和groupby
List <m_addtlallowsetup> grouped = xlist_addtlallowsetups.GroupBy(p => new { p.setupcode, p.allowcode }).Select(grp => grp.First()).OrderBy(y => y.setupcode).ThenBy(z => z.allowcode).ToList();
在我看来,这两者是相同的,但必须由外行对其差异,性能和劣势进行解释
首先,让我们回顾一下MoreLinq
API,以下是DistinctBy
的代码:
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (keySelector == null) throw new ArgumentNullException(nameof(keySelector));
return _(); IEnumerable<TSource> _()
{
var knownKeys = new HashSet<TKey>(comparer);
foreach (var element in source)
{
if (knownKeys.Add(keySelector(element)))
yield return element;
}
}
}
HashSet<T>
它只是检查第一个匹配项并返回与Key相匹配的Type T
的第一个元素,其余的都将被忽略,因为Key已经添加到HashSet中了 Func<TSource, TKey> keySelector
定义的集合中每个唯一Keyin有关的第一个元素的最简单方法 ( 源代码 )
public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector) {
return new GroupedEnumerable<TSource, TKey, TElement>(source, keySelector, elementSelector, null);
}
internal class GroupedEnumerable<TSource, TKey, TElement> : IEnumerable<IGrouping<TKey, TElement>>
{
IEnumerable<TSource> source;
Func<TSource, TKey> keySelector;
Func<TSource, TElement> elementSelector;
IEqualityComparer<TKey> comparer;
public GroupedEnumerable(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer) {
if (source == null) throw Error.ArgumentNull("source");
if (keySelector == null) throw Error.ArgumentNull("keySelector");
if (elementSelector == null) throw Error.ArgumentNull("elementSelector");
this.source = source;
this.keySelector = keySelector;
this.elementSelector = elementSelector;
this.comparer = comparer;
}
public IEnumerator<IGrouping<TKey, TElement>> GetEnumerator() {
return Lookup<TKey, TElement>.Create<TSource>(source, keySelector, elementSelector, comparer).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}
LookUp
数据结构将给定Key的所有数据分组 MoreLinq - DistinctBy
实现了Enumerable - GroupBy
可以实现的一小部分。 如果您的用例是特定的,请使用More Linq API MoreLinq - DistinctBy
会更快,因为与Enumerable - GroupBy
不同, DistinctBy
不会先汇总所有数据,然后为每个唯一的键首先选择,MoreLinq API只会忽略第一条记录以外的数据 MoreLinq
是更好的选择。 这是Linq中的经典案例,其中多个API可以提供相同的结果,但是我们需要警惕成本因素,因为此处的GroupBy
设计用于比DistinctBy
期望的任务更广泛的任务
GroupBy
应该会产生一个包含关键字(分组条件)及其值的“组”。 这就是为什么您需要首先执行Select(grp => grp.First())
。
您可能会怀疑MoreLinq只是它的简写。 通过来源了解更多信息 , DistinctBy
实际上是通过选择HashSet
每个新项在内存中完成的。 HashSet#Add
将添加item,如果它是HashSet
的新元素,则返回true,然后yield
将新添加的元素返回到可枚举中。
基于以上差异,您可以说GroupBy
然后用Select
进行投影是更安全的方法,因为如果使用的是Entity Framework(或者我想是Linq2Sql),它可以转换为SQL命令。 能够转换为SQL命令是一个很大的优势,它可以减轻应用程序的负担并将代理操作委派给数据库服务器。
但是,您必须了解,实体框架中的GroupBy
实际上使用了被视为复杂操作的OUTER JOIN
,在某些情况下,它可能会导致您的查询立即被删除。 这是非常罕见的情况,即使我抛出的查询也有很多列,使用了大约四个GroupBy
,一堆排序和Where
。
粗略地说,当处理一个已经存在于内存中的枚举数时。 然后运行GroupBy
Select
可能最终需要两次操作来迭代您的枚举。 直接使用MoreLinq的DistinctBy
可以节省一些时间,因为它保证是由HashSet支持的单个操作,正如Mrinal Kamboj回答所解释的,并针对源代码进行了深入分析。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.