17

Enumerable.GroupByandQueryable.GroupBy扩展有 8 个重载。其中两个(对于Enumerable.GroupBy)是:

// (a)
IEnumerable<TResult> GroupBy<TSource, TKey, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector,
    Func<TKey, IEnumerable<TSource>, TResult> resultSelector);

// (b)
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);

Queryable.GroupBy同样,只是用Expression<Func<...而不是Func<...

(b)有一个额外elementSelector的 as 参数。

在 MSDN 上是重载 (a)示例和重载 (b) 的示例。它们都使用相同的示例源集合:

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

示例 (a) 使用此查询:

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

示例 (b) 使用此查询:

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

两个查询的结果完全相同。

问题 1:是否有任何我无法resultSelector单独使用的查询以及我真正需要的地方elementSelector?或者这两个重载的能力是否总是相同的,使用其中一种或另一种方式只是个人喜好问题?

问题 2:在使用 LINQ 查询语法时,这两种不同的重载是否存在对应关系?

(作为一个附带问题:当Queryable.GroupBy与实体框架一起使用时,两个重载都会被翻译成完全相同的 SQL 吗?)

4

1 回答 1

19

对于 IEnumerable:

petsList.GroupBy(
    pet => Math.Floor(pet.Age), // keySelector
    (age, pets) => new          // resultSelector
    {
        Key = age,
        Count = pets.Count(),
        Min = pets.Min(pet => pet.Age),
        Max = pets.Max(pet => pet.Age)
    });

相当于:

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

使用 elementSelector 可以简化 resultSelector 中的表达式(比较下一个和上一个):

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(), //there is no lambda due to element selector
        Max = ages.Max() ////there is no lambda due to element selector
    });

在 IQueryable 中,就没有这么简单了。您可以查看此方法的来源:

public static IQueryable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, Expression<Func<TSource, TElement>> elementSelector, Expression<Func<TKey, IEnumerable<TElement>, TResult>> resultSelector)
        {
            if (source == null)
                throw Error.ArgumentNull("source"); 
            if (keySelector == null)
                throw Error.ArgumentNull("keySelector"); 
            if (elementSelector == null) 
                throw Error.ArgumentNull("elementSelector");
            if (resultSelector == null) 
                throw Error.ArgumentNull("resultSelector");
            return source.Provider.CreateQuery<TResult>(
                Expression.Call(
                    null, 
                    ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(typeof(TSource), typeof(TKey), typeof(TElement), typeof(TResult)),
                    new Expression[] { source.Expression, Expression.Quote(keySelector), Expression.Quote(elementSelector), Expression.Quote(resultSelector) } 
                    )); 
        }

public static IQueryable<TResult> GroupBy<TSource, TKey, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector,Expression<Func<TKey, IEnumerable<TSource>, TResult>> resultSelector)
        {
            if (source == null)
                throw Error.ArgumentNull("source"); 
            if (keySelector == null)
                throw Error.ArgumentNull("keySelector"); 
            if (resultSelector == null) 
                throw Error.ArgumentNull("resultSelector");
            return source.Provider.CreateQuery<TResult>( 
                Expression.Call(
                    null,
                    ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(typeof(TSource), typeof(TKey), typeof(TResult)),
                    new Expression[] { source.Expression, Expression.Quote(keySelector), Expression.Quote(resultSelector) } 
                    ));
        } 

如您所见,它们返回不同的表达式,因此我不确定结果 SQL 查询在所有情况下都相同,但我认为使用 elementSelector + resultSelector 进行重载的 SQL 查询不会比没有 elementSelector 的重载慢。

答案 1:不,对于 IEnumerable,没有无法resultSelector单独使用的查询。

回答 2。不,当使用 LINQ 查询语法时,这两种不同的重载没有对应物。与 LINQ 查询语法相比,扩展方法具有更多可能性。

答案 3(对于附带问题):不能保证 sql 查询对于此重载将是相同的。

于 2013-01-01T19:07:20.480 回答