C#扩展方法之Enumerable.Aggregate分析

开发 后端
本文介绍C#扩展方法Enumerable.Aggregate的使用。Aggregate是Enumerable类的第一个方法,但确是Enumerable里面相对复杂的方法。

Enumerable.Aggregate 扩展方法在System.Linq命名空间中,是Enumerable类的***个方法(按字母顺序排名),但确是Enumerable里面相对复杂的方法。

MSDN对这个C#扩展方法的说明是:对序列应用累加器函数。备注中还有一些说明,大意是这个方法比较复杂,一般情况下用Sum、Max、Min、Average就可以了。

看看下面的代码,有了Sum,谁还会用Aggregate呢!

  1. public static void Test1()  
  2. {  
  3.     int[] nums = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};  
  4.  
  5.     int sum1 = nums.Sum();  
  6.     int sum2 = nums.Aggregate((i,j)=>i+j);  
  7. }  

同是求和,Sum不再需要额外参数,而Aggregate确还要将一个lambda作为参数。因为用起来麻烦,操作太低级,Aggregate渐渐被大多人忽视了...

实际上Aggregate因为“低级”,功能确是很强大的,通过它可以简化很多聚合运算。

首先来看对Aggregate组装字符串的问题:

  1. public static void Test2()  
  2. {  
  3.     string[] words = new string[] { "Able""was""I""ere""I""saw""Elba"};  
  4.     string s = words.Aggregate((a, n) => a + " " + n);  
  5.     Console.WriteLine(s);  

输出结果是:Able was I ere I saw Elba (注:出自《大国崛起》,狄娜***讲述了拿破仑一句经典)。

当然考虑性能的话还是用StringBuilder吧,这里主要介绍用法。这个Sum做不到吧!

Aggregate还可以将所有字符串倒序累加,配合String.Reverse扩展可以实现整个句子的倒序输出:

  1. public static void Test3()  
  2. {  
  3.     string[] words = new string[] { "Able""was""I""ere""I""saw""Elba"};  
  4.     string normal = words.Aggregate((a, n) => a + " " + n);  
  5.     string reverse = words.Aggregate((a, n) => n.Reverse() + " " + a);  
  6.  
  7.     Console.WriteLine("正常:" + normal);  
  8.     Console.WriteLine("倒置:" + reverse);  
  9. }  
  10. // 倒置字符串,输入"abcd123",返回"321dcba"  
  11. public static string Reverse(this string value)  
  12. {  
  13.     char[] input = value.ToCharArray();  
  14.     char[] output = new char[value.Length];  
  15.     for (int i = 0; i < input.Length; i++)  
  16.         output[input.Length - 1 - i] = input[i];  
  17.     return new string(output);  
  18. }  

看下面,输出结果好像不太对:

输出结果 

怎么中间的都一样,两的单词首尾字母大小写发生转换了呢?!

仔细看看吧,不是算法有问题,是输入“有问题”。搜索一下“Able was I ere I saw Elba”,这可是很有名的英文句子噢!

Enumerable.Aggregate这个C#扩展方法还可以实现异或(^)操作:

  1. public static void Test4()  
  2. {  
  3.     byte[] data = new byte[] { 0x31, 0x32, 0x33, 0x34, 0x35 };  
  4.     byte checkSum = data.Aggregate((a, n) => (byte)(a ^ n));  
  5. }  

对经常作串口通信的朋友比较实用。

看来Aggregate也是比较“简单易用”的,深入一步来看看它是怎么实现的吧,使用Reflector,反编译一下System.Core.dll。

以下代码取自反编译结果,为了演示删除了其中的空值判断代码:

  1. public static TSource Aggregate(this IEnumerable source, Func func)  
  2.         {  
  3.             using (IEnumerator enumerator = source.GetEnumerator())  
  4.             {  
  5.                 enumerator.MoveNext();  
  6.                 TSource current = enumerator.Current;  
  7.                 while (enumerator.MoveNext())  
  8.                     current = func(current, enumerator.Current);  
  9.                 return current;  
  10.             }  
  11.         } 

也很简单吧,就是一个循环!前面lambda表达式中参数a, n 分别对应current, enumerator.Current,对照一下,还是很好理解的。

现在我们想求整数数组中位置为偶数的数的和(间隔求和),可以用Where配合Sum:

  1. public static void Test5()  
  2. {  
  3.     int[] nums = new int[] { 10, 20, 30, 40, 50 };  
  4.     int sum1 = nums.Where((n, i) => i % 2 == 0).Sum();//10 + 30 + 50  

这个Where扩展设计的很好,它不但能带出某项的值“n”,还能带出项的位置“i”。

Aggregate可不行!我们来改进一下:

  1. //改进的Aggerate扩展(示例代码,实际使用请添加空值检查)  
  2. public static TSource Aggregate(this IEnumerable source, Funcint, TSource> func)  
  3. {  
  4.     int index = 0;  
  5.     using (IEnumerator enumerator = source.GetEnumerator())  
  6.     {  
  7.         enumerator.MoveNext();  
  8.         index++;  
  9.         TSource current = enumerator.Current;  
  10.         while (enumerator.MoveNext())  
  11.             current = func(current, enumerator.Current, index++);  
  12.         return current;  
  13.     }  

改进后的Aggregate更加强大,前面的求偶数位置数和的算法可以写成下面的样子:

  1. public static void Test6()  
  2. {  
  3.     int[] nums = new int[] { 10, 20, 30, 40, 50 };  
  4.     int sum2 = nums.Aggregate((a, c, i) => a + i%2 == 0 ? c : 0 );//10 + 30 + 50  

可能不够简洁,但它一个函数代替了Where和Sum。所在位置“i“的引入给Aggregate带来了很多新的活力,也增加了它的应用范围!

我随笔《使用“初中知识”实现查找重复***算法 + 最***限算法》中***提出的“最***限算法”,用上这里改进的Aggregate扩展,也可以甩开Select和Sum,更加精简一步了:

  1. public static void Test7()  
  2. {  
  3.     //1~n放在含有n+1个元素的数组中,只有唯一的一个元素值重复,最简算法找出重复的数  
  4.     int[] array = new int[] { 1, 3, 2, 3, 4, 5 };  
  5.     //原极限算法  
  6.     int repeatedNum1 = array.Select((i, j) => i - j).Sum();  
  7.     //***极限算法  
  8.     int repeatedNum2 = array.Aggregate((a, n, i) => a + n - i);  
  9. }  

以上就介绍了C#扩展方法之Enumerable.Aggregate。本文来自鹤冲天的博客:《c#扩展方法奇思妙用高级篇二:Aggregate扩展其改进》。

【编辑推荐】

  1. 有关继承与扩展方法之比较:ObservableCollection
  2. C#继承知识详解
  3. 浅谈C#如何实现多继承
  4. .NET 3.5扩展方法点评:优点与问题
  5. 浅析C#扩展方法
责任编辑:yangsai 来源: 鹤冲天
相关推荐

2009-08-10 17:36:17

C#扩展方法

2009-08-27 18:04:01

c#扩展方法string

2009-09-01 11:04:59

C#调用扩展方法

2009-08-31 14:45:10

C#扩展方法

2009-08-26 15:53:48

C#扩展方法

2009-08-27 09:27:49

C#扩展方法

2009-08-18 14:14:45

C#扩展方法性能测试

2009-08-27 16:24:48

扩展方法C# 3.0新特性

2012-10-31 17:37:48

2009-08-05 15:04:14

C# dll注入

2009-04-03 13:20:05

C#扩展方法调用

2009-08-18 14:36:36

C# 操作Excel

2009-09-01 11:19:47

C# 3.0扩展重载抉

2009-08-25 17:59:49

C#入门

2009-09-17 11:29:50

Linq扩展方法

2009-09-02 17:08:30

C#语言开发Windo

2009-08-28 14:25:57

C# byte数组

2012-11-09 10:47:21

LINQ

2009-08-26 09:50:08

C# GreetPeo

2009-08-26 17:16:22

C# CheckSta
点赞
收藏

51CTO技术栈公众号