2013-01-01 76 views
14

Enumerable.GroupByQueryable.GroupBy扩展有8个重载。它们(Enumerable.GroupBy)中有两个是:GroupBy with elementSelector and resultSelector

// (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实体框架,都将过载被翻译成完全相同的SQL?)

回答

15

对于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) 
    }); 

是equevalent到:

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查询将在所有情况下都一样,但我想 SQL与没有elementSelector的过载相比,使用elementSelector + resultSelector查询过载不会更慢。

回答1:不,对于IEnumerable,不存在通过单独使用resultSelector无法表达的查询。

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

回答3(对于侧面问题):不能保证sql查询对于这个重载是相同的。