首页 / 教程列表 / LINQ教程 / LINQ操作符之Aggregate

LINQ操作符之Aggregate

843 更新于: 2022-04-24 读完约需 17 分钟

LINQ中的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);

下面我们以具体的示例来一步一步学习LINQ的Aggregate聚合操作符。

比如有如下的数字类型的数组:

var numbers = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

现要求对数组numbers进行求和(用C#语言实现),你会想到的做法是什么呢?

传统的循环遍历方式

在没有学习LINQ的聚合操作符之前,我们通常的做法可能是循环遍历的方式来实现上述的需求,示例代码如下:

using System;

namespace LinqTutorial
{
    public class MyProgram
    {
        static void Main(string[] args)
        {
            var numbers = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            var sum = 0;
            for (var i = 0; i < numbers.Length; i++)
            {
                sum += numbers[i];
            }
            Console.WriteLine(sum);
            Console.Read();
        }
    }
}

运行得到的求和结果为:45

LINQ的Aggregate聚合

以上使用统计的循环遍历实现得到的最终结果是没有问题的,但过程相当繁琐。这样的场景,我们可以借助.NET内置的Aggregate聚合操作符进行求和计算,示例代码如下:

using System;
using System.Linq;

namespace LinqTutorial
{
    public class MyProgram
    {
        static void Main(string[] args)
        {
            var numbers = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            var sum = numbers.Aggregate((num, i) => num + i);
            Console.WriteLine(sum);
            Console.Read();
        }
    }
}

可以看到,使用Aggregate聚合操作符仅一行代码就实现了循环遍历的工作,无疑可以提高开发者的生产效率。

这也是C#语言优雅语法的真正体现:用更少的、更优美的代码,实现相同的功能需求。

读到这里,我可能对Aggregate聚合操作符还是一头雾水。没关系,下面我们按照Aggregate的执行步骤对上例一步一步进行分解。

示例代码中,Aggregate聚合方法中的lambda表达式(num, i) => num + i在执行时可以表达到这样的: num = num + i,其中num将作为数组中的每个项的累积。
因此,Aggregate方法将返回数组的累加和。下面请看累加操作是如何一步步执行的:

  1. 第1步,将numbers集合中的第一个元素(1)赋值给num,第二个元素2赋值给i,进行表达式num + i的运算,并将运算得到的结果(1+2=3)返回。
  2. 第2步,将上一步得到的结果再次赋值给numnumbers集合的下次迭代元素3(这里应该是第2次迭代)赋值给i,进行表达式num + i的运算,将运算得到的结果(3+3=6)返回。
  3. 以次类推,直到把numbers集合的元素迭代完为止,最后返回所有元素累加的结果。

为了方便直观查看Aggregate方法的变量,我们在Aggregate方法中添加变量的输出日志,示例如下:

using System;
using System.Linq;

namespace LinqTutorial
{
    public class MyProgram
    {
        static void Main(string[] args)
        {
            var numbers = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            var sum = numbers.Aggregate((num, i) =>
            {
                var val = num + i;
                Console.WriteLine($"num({num}) + i({i}) = {val}");
                return val;
            });
            Console.WriteLine(sum);
            Console.Read();
        }
    }
}

输出结果如下:

有种子参数的Aggregate重载方法

Aggregate方法的第二个重载是将第一个参数作为要累积的种子值,第二个参数是Func类型的委托(delegate),还是以上面做1到9的累加求和为例,只是这里使用有种参数的重载方法,语法示例如下:

var sum = numbers.Aggregate(2, (num, i) => num + i);

为了方便观察Aggregate方法的执行过程,同样的,我们将变量输出到了控制台。完整示例如下:

using System;
using System.Linq;

namespace LinqTutorial
{
    public class MyProgram
    {
        static void Main(string[] args)
        {
            var numbers = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            var sum = numbers.Aggregate(2, (num, i) =>
              {
                  var val = num + i;
                  Console.WriteLine($"num({num}) + i({i}) = {val}");
                  return val;
              });
            Console.WriteLine(sum);
            Console.Read();
        }
    }
}

运行结果如下:

从运行结果可以看出,有种子参数的Aggregate方法重载比没有的多执行了一次,即第1次:num(2) + i(1) = 3。也就是说,如果有种子值,则Aggregate方法将在执行第1次迭代时使用种子值,之后再依次迭代集合的每个元素。

Aggregate拼接字符串

当然,Aggregate扩展方法不止能做数学运算,也可以处理字符,比如字符串的拼接问题,示例如下:

using System;
using System.Linq;

namespace LinqTutorial
{
    public class MyProgram
    {
        static void Main(string[] args)
        {
            var numbers = new[] { "One", "Two", "Three", "Four", "Five" };
            var result = numbers.Aggregate((current, index) => current + "," + index);
            Console.WriteLine(result);
            Console.Read();
        }
    }
}

运行结果:

One,Two,Three,Four,Five

除了正序拼接字符串,还可以反序拼接字符串,示例代码如下:

var result = numbers.Aggregate((current, index) => index + "," + current);

提示:你看出正序和反序拼接字符串实现方式的区别了吗?

运行结果:

Five,Four,Three,Two,One

Aggregate的结果选择器参数

Aggregate方法的第3个重载需要结果选择器的Func委托表达式的作为第三个形参,以便对聚合的结果进行进一步的处理,这里还是以将字符串集合拼接成以逗号隔开的字符串为例,示例代码如下:

using System;
using System.Linq;

namespace LinqTutorial
{
    public class MyProgram
    {
        static void Main(string[] args)
        {
            var numbers = new[] { "One", "Two", "Three", "Four", "Five" };
            var result = numbers.Aggregate("", (current, index) => current + "," + index);
            Console.WriteLine(result);
            Console.Read();
        }
    }
}

运行的结果:

,One,Two,Three,Four,Five

注:因为第1个参数的种子值是空字符串,所以拼接出来的结果以逗号开头。

现在,我们借助Aggregate的第3个参数,对结果进行处理,即去掉开头多余的逗号,示例如下:

using System;
using System.Linq;

namespace LinqTutorial
{
    public class MyProgram
    {
        static void Main(string[] args)
        {
            var numbers = new[] { "One", "Two", "Three", "Four", "Five" };
            var result = numbers.Aggregate("",
                (current, index) => current + "," + index
                , str => str.TrimStart(','));
            Console.WriteLine(result);
            Console.Read();
        }
    }
}

运行结果:

One,Two,Three,Four,Five

Aggregate总结

本文详细介绍了LINQ中Aggregate聚合方法及重载的不同场景的使用方法。希望以上内容能对你的Aggregate学习带来一些启发和帮助。

版权声明:本作品系原创,版权归码友网所有,如未经许可,禁止任何形式转载,违者必究。

本文永久链接码友网 » LINQ教程 » LINQ操作符之Aggregate 分享:

发表评论

登录用户才能发表评论, 请 登 录 或者 注册