Linq的GroupBy方法在resultselector重载时有什么用途?

时间:2015-11-24 23:11:15

标签: c# linq

在MSDN上,我发现以下Enumerable.GroupBy方法的重载:

public static IEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TSource, TElement> elementSelector,
Func<TKey, IEnumerable<TElement>, TResult> resultSelector)

以及以下示例:

    List<Pet> petsList =
    new List<Pet>{ new Pet { Name="Barley", Age=8.3 },
                   new Pet { Name="Boots", Age=4.9 },
                   new Pet { Name="Whiskers", Age=1.5 },
                   new Pet { Name="Daisy", Age=4.3 } };

    var query = petsList.GroupBy(
    pet => Math.Floor(pet.Age), //keySelector
    pet => pet.Age,             //elementSelector
    (baseAge, ages) => new      //resultSelector
    {
        Key = baseAge,
        Count = ages.Count(),
        Min = ages.Min(),
        Max = ages.Max()
    });

虽然我理解这段代码是如何工作的,但我无法想到它的实际用途。正如我现在看到的,这将返回Enumerable,其中包含在代码末尾定义的匿名类型的元素。但为什么 GroupBy 方法会返回一个不分组的可枚举数?使用Select,OrderBy等可以实现同样的目标吗?或者这个重载的主要目的是将Dictionary自己定义为TResult的一部分?

2 个答案:

答案 0 :(得分:4)

以下两个查询将产生相同的结果。

var queryA = source
    .GroupBy(x => x.Id)
    .Select(g => new { Id = g.Key, Count = g.Count() });

var queryB = source
    .GroupBy(x => x.Id, (key, g) => new { Id = key, Count = g.Count() });

不同之处在于第一个查询必须为每个组实例化IGrouping<TKey, TElement>,而第二个查询则不包含queryB。在这种情况下,您应该更喜欢!DumpHeap -stat

答案 1 :(得分:3)

是的,它不能用Select()做任何事情。我们甚至可以将其实现为:

public static IEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<TKey, IEnumerable<TElement>, TResult> resultSelector)
{
  return source.GroupBy(keySelector, elementSelector).Select(grp => resultSelector(grp.Key, grp));
}

/* Repeat in Queryable for IQueryable */

但是,首先,它没有以这种方式实现,而且这种相对常见的情况得到了更有效的处理。

其次,我怀疑更大的影响力(但当然不能肯定地说)是IQueryable版本类似于许多SQL组构造。实际上,对于大多数SQL GROUP BY,我们必须使用聚合函数和GROUP BY中未包含的所选项。考虑:

SELECT FLOOR(age), COUNT(ALL age), MIN(age), MAX(age)
FROM Pets
GROUP BY FLOOR(age)

这与您引用的示例中的基于列表的操作非常相似。

因此,Queryable.GroupBy()的那些带有resultSelector参数的重载是有意义的,当Queryable作用于IQueryable时,相应的方法必须Enumerable代理IEnumerable