Enumerable.GroupBy
和Queryable.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
作为参数。
在MSDN上有an example for overload (a)和an example for overload (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
与Entity Framework一起使用时,是否会将两个重载都转换为完全相同的SQL?)
答案 0 :(得分:18)
对于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中的表达式(比较next和previous):
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查询对于此重载是相同的。