65.9K
CodeProject 正在变化。 阅读更多。
Home

Aggregate,真正的聚合 LinQ 运算符

2018年3月6日

CPOL

3分钟阅读

viewsIcon

7961

downloadIcon

75

回顾 LinQ 聚合运算符,并展示所有运算符都可以使用 Aggregate 运算符构建。

引言

Aggregate LinQ 运算符在 LinQ 世界中是一个未知数。该项目的目标是回顾 LinQ 聚合运算符 (Count, LongCount, Min, Max, SumAverage),并 展示所有运算符都可以使用 Aggregate 运算符构建

目录

  • CountLongCount
  • Sum
  • Min y Max
  • Average
  • Aggregate

之前的运算符 Aggregate 实现

  • Sum
  • 最小值
  • 最大值
  • Average

Count 和 LongCount

扩展方法 CountLongCount 返回序列的元素数量。CountLongCount 之间的区别在于返回类型。Count 运算符返回一个 int (System.Int32) 类型,并且您的限制是 2,147,483,647 个元素。LongCount 运算符返回一个 Long (System.Int64) 类型,并且您的限制远大于 Count 运算符 9,223,372,036,854,775,807 个元素。对于日常使用,Count 运算符应该足够了。

签名

public static int Count<TSource>(this IEnumerable<TSource> source);
public static int Count<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
 
public static long LongCount<TSource>(this IEnumerable<TSource> source);
public static long LongCount<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);

它们都有两个重载。第一个重载是一个简单的方法,用于计算序列中的所有元素。第二个重载有一个谓词参数 (Func<TSource, bool>),用于仅计算序列的一部分。

示例

public static void CountAndLongCountTests()
{
    var elements = new List<Element>()
    {
        new Element{ ID = 0, Amount = 10000},
        new Element{ ID = 1, Amount =  1000},
        new Element{ ID = 2, Amount =  2000},
        new Element{ ID = 3, Amount =  3000}
    };
 
 
    var numSmall = elements.Count();
    var numBig   = elements.LongCount();
 
    var numSmallFiltered = elements.    Count(e => e.Amount >= 2000);
    var numBigFiltered   = elements.LongCount(e => e.Amount >= 2000);
 
 
    Console.WriteLine($"{nameof(numSmallFiltered)} type: {numSmallFiltered.GetType().Name}");
    Console.WriteLine($"{nameof(numBigFiltered)}   type: {numBigFiltered.GetType()  .Name}");
 
    Console.Read();
}

控制台结果

结果相同,只是更改了 datatype

LongCont 仅应用于非常大的集合。

Sum

Sum 扩展方法返回元素序列的 sum。这些元素可以为 null

Sum 运算符有 20 个重载,分为 2 组。

第一组由只有单个参数的方法组成。此参数始终是数字,并且与源集合类型和返回类型相同。

签名组 1

public static long     Sum(this IEnumerable<long>     source);
public static long?    Sum(this IEnumerable<long?>    source);
public static float?   Sum(this IEnumerable<float?>   source);
public static double   Sum(this IEnumerable<double>   source);
public static double?  Sum(this IEnumerable<double?>  source);
public static decimal? Sum(this IEnumerable<decimal?> source);
public static decimal  Sum(this IEnumerable<decimal>  source);
public static float    Sum(this IEnumerable<float>    source);
public static int?     Sum(this IEnumerable<int?>     source);
public static int      Sum(this IEnumerable<int>      source);

示例

public static void SumTests1()
{
    var elements = new List<Element>()
    {
        new Element{ ID = 0, Amount = 10000},
        new Element{ ID = 1, Amount =  1000},
        new Element{ ID = 2, Amount =  2000},
        new Element{ ID = 3, Amount =  3000}
    };
 
    int[] numbers = new int[] { 1000, 2000, 3000 };
 
 
    int resultNumbers = numbers.Sum();
 
    var sumAmount = elements.Select(a => a.Amount).Sum();
 
    Console.WriteLine($"{nameof(resultNumbers)}   = {resultNumbers}");
    Console.WriteLine($"{nameof(sumAmount)}       = {sumAmount}");
 
    Console.Read();
}

控制台结果

这些调用不包含参数,因为它是一个扩展方法参数。

第二组也有 10 个方法,在这种情况下,它们是带有两个参数的泛型方法。第一个参数与第一组相同。第二个 selector 参数等于 Select 扩展方法。

签名组 2

public static float?   Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, float?>   selector);
public static double?  Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, double?>  selector);
public static decimal  Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, decimal>  selector);
public static decimal? Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, decimal?> selector);
public static double   Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, double>   selector);
public static int?     Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, int?>     selector);
public static long?    Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, long?>    selector);
public static float    Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, float>    selector);
public static long     Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, long>     selector);
public static int      Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, int>      selector);

示例

public static void SumTests2()
{
    var elements = new List<Element>()
    {
        new Element { ID = 0, Amount = 10000},
        new Element { ID = 1, Amount =  1000},
        new Element { ID = 2, Amount =  2000},
        new Element { ID = 3, Amount =  3000}
    };
 
    var sumAmount = elements.Sum(a => a.Amount);
 
    Console.WriteLine($"{nameof(sumAmount)} = {sumAmount}");
 
    Console.Read();
}

我们添加一个 Func<Element, int> 参数。

控制台结果

对于 null 值,该过程将理解为,null = 0

public static void SumTestsNull()
{
    var elements = new List<Element>()
    {
        new Element { ID = 0, Amount = null},
        new Element { ID = 1, Amount = null},
        new Element { ID = 2, Amount = null},
        new Element { ID = 3, Amount = null}
    };
 
    var sumAmount = elements.Sum(a => a.Amount);
 
    Console.WriteLine($"{nameof(sumAmount)} = {sumAmount}");
 
    Console.Read();
}

控制台结果

Min 和 Max

MinMax 运算符与 Sum 运算符具有相同的特性,但目标不同。Max 获取序列中的最大值,而 Min 获取最小值。

签名

public static double?   Max(this IEnumerable<double?>  source);
public static double    Max(this IEnumerable<double>   source);
public static long?     Max(this IEnumerable<long?>    source);
public static long      Max(this IEnumerable<long>     source);
public static int?      Max(this IEnumerable<int?>     source);
public static float     Max(this IEnumerable<float>    source);
public static float?    Max(this IEnumerable<float?>   source);
public static int       Max(this IEnumerable<int>      source);
public decimal          Max(this IEnumerable<decimal>  source);
public decimal?         Max(this IEnumerable<decimal?> source);
 
public static long?     Min(this IEnumerable<long?>    source);
public static int?      Min(this IEnumerable<int?>     source);
public static int       Min(this IEnumerable<int>      source);
public static float?    Min(this IEnumerable<float?>   source);
public static double    Min(this IEnumerable<double>   source);
public static double?   Min(this IEnumerable<double?>  source);
public static decimal   Min(this IEnumerable<decimal>  source);
public static decimal?  Min(this IEnumerable<decimal?> source);
public static long      Min(this IEnumerable<long>     source);
public static float     Min(this IEnumerable<float>    source);
 
 
public static int       Max<TSource>(this IEnumerable<TSource> source, 
                        Func<TSource, int>      selector);
public static int?      Max<TSource>(this IEnumerable<TSource> source, 
                        Func<TSource, int?>     selector);
public static long      Max<TSource>(this IEnumerable<TSource> source, 
                        Func<TSource, long>     selector);
public static long?     Max<TSource>(this IEnumerable<TSource> source, 
                        Func<TSource, long?>    selector);
public static float     Max<TSource>(this IEnumerable<TSource> source, 
                        Func<TSource, float>    selector);
public static float?    Max<TSource>(this IEnumerable<TSource> source, 
                        Func<TSource, float?>   selector);
public static double    Max<TSource>(this IEnumerable<TSource> source, 
                        Func<TSource, double>   selector);
public static double?   Max<TSource>(this IEnumerable<TSource> source, 
                        Func<TSource, double?>  selector);
public static decimal   Max<TSource>(this IEnumerable<TSource> source, 
                        Func<TSource, decimal>  selector);
public static decimal?  Max<TSource>(this IEnumerable<TSource> source, 
                        Func<TSource, decimal?> selector);
 
public static long?     Min<TSource>(this IEnumerable<TSource> source, 
                        Func<TSource, long?>    selector);
public static decimal?  Min<TSource>(this IEnumerable<TSource> source, 
                        Func<TSource, decimal?> selector);
public static decimal   Min<TSource>(this IEnumerable<TSource> source, 
                        Func<TSource, decimal>  selector);
public static float     Min<TSource>(this IEnumerable<TSource> source, 
                        Func<TSource, float>    selector);
public static double?   Min<TSource>(this IEnumerable<TSource> source, 
                        Func<TSource, double?>  selector);
public static int?      Min<TSource>(this IEnumerable<TSource> source, 
                        Func<TSource, int?>     selector);
public static int       Min<TSource>(this IEnumerable<TSource> source, 
                        Func<TSource, int>      selector);
public static float?    Min<TSource>(this IEnumerable<TSource> source, 
                        Func<TSource, float?>   selector);
public static long      Min<TSource>(this IEnumerable<TSource> source, 
                        Func<TSource, long>     selector);
public static double    Min<TSource>(this IEnumerable<TSource> source, 
                        Func<TSource, double>   selector);

简单示例

public static void MinMaxTests()
{
    int[] numbers = new int[] { 1000, 2000, 3000 };
 
    int maxNumbers = numbers.Max();
    int minNumbers = numbers.Min();
 
 
    Console.WriteLine($"{nameof(maxNumbers) } = {maxNumbers }");
    Console.WriteLine($"{nameof(minNumbers) } = {minNumbers }");
 
    Console.Read();
}

结果

复杂示例

public static void MinMaxTests2()
{
    var elements = new List<Element>()
    {
        new Element { ID = 0, Amount = 10000},
        new Element { ID = 1, Amount =  1000},
        new Element { ID = 2, Amount =  2000},
        new Element { ID = 3, Amount =  3000}
    };
 
    var maxAmountForSelect = elements.Max(a => a.Amount);
    var minAmountForSelect = elements.Min(a => a.Amount);
 
    var maxAmountForField = elements.Max(a => a.Amount);
    var minAmountForField = elements.Min(a => a.Amount);
 
    Console.WriteLine($"{nameof(maxAmountForSelect)} = {maxAmountForSelect}");
    Console.WriteLine($"{nameof(minAmountForSelect)} = {minAmountForSelect}");
    Console.WriteLine($"{nameof(maxAmountForField) } = {maxAmountForField }");
    Console.WriteLine($"{nameof(minAmountForField) } = {minAmountForField }");
 
    Console.Read();
}

结果

Average

与他的兄弟们(Sum, MaxMin)相同的公式、行为和处理,但用于查找平均值。

在这种情况下,返回类型始终是带有小数部分的类型,如 decimal 或 double。

签名

public static float    Average(this IEnumerable<float>    source);
public static double?  Average(this IEnumerable<long?>    source);
public static float?   Average(this IEnumerable<float?>   source);
public static double   Average(this IEnumerable<double>   source);
public static double   Average(this IEnumerable<int>      source);
public static decimal  Average(this IEnumerable<decimal>  source);
public static decimal? Average(this IEnumerable<decimal?> source);
public static double   Average(this IEnumerable<long>     source);
public static double?  Average(this IEnumerable<double?>  source);
public static double?  Average(this IEnumerable<int?>     source);

public static decimal  Average<TSource>(this IEnumerable<TSource> source, 
                       Func<TSource, decimal>  selector);
public static decimal? Average<TSource>(this IEnumerable<TSource> source, 
                       Func<TSource, decimal?> selector);
public static double?  Average<TSource>(this IEnumerable<TSource> source, 
                       Func<TSource, int?>     selector);
public static double   Average<TSource>(this IEnumerable<TSource> source, 
                       Func<TSource, long>     selector);
public static double?  Average<TSource>(this IEnumerable<TSource> source, 
                       Func<TSource, long?>    selector);
public static float    Average<TSource>(this IEnumerable<TSource> source, 
                       Func<TSource, float>    selector);
public static float?   Average<TSource>(this IEnumerable<TSource> source, 
                       Func<TSource, float?>   selector);
public static double   Average<TSource>(this IEnumerable<TSource> source, 
                       Func<TSource, double>   selector);
public static double?  Average<TSource>(this IEnumerable<TSource> source, 
                       Func<TSource, double?>  selector);
public static double   Average<TSource>(this IEnumerable<TSource> source, 
                       Func<TSource, int>      selector);

简单示例

public static void Average1()
{
    int[] numbers = new int[] { 1000, 2000, 3000 };
 
    var mediaNumbers = numbers.Average();
 
    Console.WriteLine($"{nameof(mediaNumbers) } = {mediaNumbers }");
 
    Console.Read();
}

结果

复杂示例

public static void Average2()
{
    var elements = new List<Element>()
    {
        new Element { ID = 0, Amount = 10000},
        new Element { ID = 1, Amount =  1000},
        new Element { ID = 2, Amount =  2000},
        new Element { ID = 3, Amount =  3000}
    };
 
    var mediaForSelect = elements.Max(a => a.Amount);
 
    var mediaForField = elements.Max(a => a.Amount);
 
 
    Console.WriteLine($"{nameof(mediaForSelect)} = {mediaForSelect}");
    Console.WriteLine($"{nameof(mediaForField) } = {mediaForField}");
 
 
    Console.Read();
}

结果

Aggregate

简而言之,扩展方法 Aggregate 用于累加函数。

Aggregate 有三个签名

public static TSource Aggregate<TSource>
(this IEnumerable<TSource> source, Func<TSource, TSource, TSource> func);

public static TAccumulate Aggregate<TSource, TAccumulate>
(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func);

public static TResult Aggregate<TSource, TAccumulate, TResult>
(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, 
TSource, TAccumulate> func, Func<TAccumulate, TResult> resultSelector);

第一个签名

这是最简单的签名。Aggregate 运算符调用委托匿名 Func<TAccumulate, TSource, TAccumulate> func(累加函数),对集合中的每个元素执行。

public static void Aggregate1()
{
    string[] names = { "Mike", "Julie", "John", "Laura", "Other name" };
 
    string result = names.Aggregate((resultAcum, next) => 
                    resultAcum += string.Format("\r\n{0:-16}", next));
 
    /// This is the translate in simple bucle
    //string result = string.Empty;
 
    //foreach (var next in nombres)
    //{
    //    result += string.Format("\r\n{0:-16}", next);
    //}
 
 
    Console.WriteLine(result);
 
    Console.Read();
}

结果

第二个签名

第二个签名与第一个签名相同,但第二个签名有一个新的 TSource 类型的参数。此参数显示初始累加值。

示例

public static void Aggregate2()
{
    string[] names = { "Mike", "Julie", "John", "Laura", "Other name" };
 
    string result = names.Aggregate("Inital Date", 
                    (resultAcum, next) => resultAcum += string.Format("\r\n{0:-16}", next));
 
 
    /// This is the translate in simple bucle
    //string result = "Initial Date";
 
    //foreach (var next in nombres)
    //{
    //    result += string.Format("\r\n{0:-16}", next);
    //}
 
 
    Console.WriteLine(result);
 
    Console.Read();
}

结果

第三个签名

最后一个签名具有与前一个签名相同的特征,但添加了一个新参数,用于配置输出格式。

简单示例

public static void Aggregate3()
{
    string[] names = { "Mike", "Julie", "John", "Laura", "Other name" };
 
    string result = names.Aggregate("Initial Date", 
                    (resultAcum, next) => resultAcum += string.Format("\r\n{0:-16}", next));
 
    string resultado = names.Aggregate("Inital Date",
                                (resultAcum, next) => resultAcum += string.Format("\r\n{0:-16}", next),
                                a => $"{a} \r\n --> Total characters:  {a.Length} "
                                );
 
    Console.WriteLine(resultado);
 
    Console.Read();
}

结果

复杂示例

public static void Aggregate4()
{
    string[] names = { "Mike", "Julie", "John", "Laura", "Other name" };
 
    var separador = new string[] { "\r\n" };
 
    var result = names.Aggregate("Inital Date",
                  (resultAcum, next) => resultAcum += string.Format("\r\n{0:-16}", next),
                  // We create an Anonymous type foreach element
                  a => a.Split(separador, StringSplitOptions.None).Select
                       (b => new { Dato = b, Len = b.Length })
                  );
 
    result.ToList().ForEach(r => Console.WriteLine(r));
 
    Console.Read();
}

结果

看到最后一个参数的目标非常重要。在此示例中,我们将名称字符串拆分,用于创建一个具有两个属性的新匿名对象:Dato = (string name) 和 Len = (dato 的长度)。

仅使用 Aggregate 重新创建所有运算符

现在我们将演示 Aggregate 是最重要的运算符,并且我们将使用此运算符创建所有先前的运算符。

Sum

public static void Sum_Aggregate()
{ 
    int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    var sum = numbers.Aggregate((total, next) => total += next);
    Console.WriteLine $"The {nameof(sum)} value is {sum}");
    Console.Read();
}

结果。

最小值

public static void Min_Aggregate()
{ 
    int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
 
    var min = numbers.Aggregate((total, next) => next < total ? next : total);
 
    Console.WriteLine($"The {nameof(min)} value is {min}");
 
    Console.Read();
}

结果

最大值

public static void Max_Aggregate()
{
 
    int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
 
    var max = numbers.Aggregate((total, next) => next > total ? next : total);
 
    Console.WriteLine($"The {nameof(max)} value is {max}");
 
    Console.Read();
}

结果

Average

public static void Average_Aggregate()
{ 
    int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
 
    var average = numbers.Aggregate(
                                0,
                                (total, next) => total += next,
                                a => decimal.Parse(a.ToString()) / 
                                     decimal.Parse(numbers.Count().ToString())
                            );
 
    Console.WriteLine($"The {nameof(average)} value is {average}");
 
    Console.Read();
}

结果

© . All rights reserved.