VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > temp > C#教程 >
  • c#中Aggregate扩展与改进

制作者:剑锋冷月 单位:无忧统计网,www.51stat.net
 

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

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

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

        public static void Test1()
        {
            int[] nums = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

            int sum1 = nums.Sum();
            int sum2 = nums.Aggregate((i,j)=>i+j);
        }

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

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

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

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

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

编缉推荐阅读以下文章

  • c#扩展方法奇思妙用高级篇三:Enumerable.Cast应用
  • c#扩展方法奇思妙用高级篇一:改进 Scottgu 的 "In" 扩展
 

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

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

        public static void Test3()
        {
            string[] words = new string[] { "Able", "was", "I", "ere", "I", "saw", "Elba"};
            string normal = words.Aggregate((a, n) => a + " " + n);
            string reverse = words.Aggregate((a, n) => n.Reverse() + " " + a);

            Console.WriteLine("正常:" + normal);
            Console.WriteLine("倒置:" + reverse);
        }
        // 倒置字符串,输入"abcd123",返回"321dcba"
        public static string Reverse(this string value)
        {
            char[] input = value.ToCharArray();
            char[] output = new char[value.Length];
            for (int i = 0; i < input.Length; i++)
                output[input.Length - 1 - i] = input[i];
            return new string(output);
        }

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

c#扩展方法奇思妙用高级篇二:Aggregate扩展其改进

编缉推荐阅读以下文章

  • c#扩展方法奇思妙用高级篇三:Enumerable.Cast应用
  • c#扩展方法奇思妙用高级篇一:改进 Scottgu 的 "In" 扩展
 

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

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

  Aggregate还可以实现异或(^)操作:

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

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

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

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

  Aggregate

        public static TSource Aggregate<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, TSource> func)
        {
            using (IEnumerator<TSource> enumerator = source.GetEnumerator())
            {
                enumerator.MoveNext();
                TSource current = enumerator.Current;
                while (enumerator.MoveNext())
                    current = func(current, enumerator.Current);
                return current;
            }
        }

编缉推荐阅读以下文章

  • c#扩展方法奇思妙用高级篇三:Enumerable.Cast应用
  • c#扩展方法奇思妙用高级篇一:改进 Scottgu 的 "In" 扩展
 

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

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

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

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

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

        //改进的Aggerate扩展(示例代码,实际使用请添加空值检查)
        public static TSource Aggregate<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, int, TSource> func)
        {
            int index = 0;
            using (IEnumerator<TSource> enumerator = source.GetEnumerator())
            {
                enumerator.MoveNext();
                index++;
                TSource current = enumerator.Current;
                while (enumerator.MoveNext())
                    current = func(current, enumerator.Current, index++);
                return current;
            }
        }

 

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

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

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

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

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

 

编缉推荐阅读以下文章

  • c#扩展方法奇思妙用高级篇三:Enumerable.Cast应用
  • c#扩展方法奇思妙用高级篇一:改进 Scottgu 的 "In" 扩展
 

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

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

        public static void Test3()
        {
            string[] words = new string[] { "Able", "was", "I", "ere", "I", "saw", "Elba"};
            string normal = words.Aggregate((a, n) => a + " " + n);
            string reverse = words.Aggregate((a, n) => n.Reverse() + " " + a);

            Console.WriteLine("正常:" + normal);
            Console.WriteLine("倒置:" + reverse);
        }
        // 倒置字符串,输入"abcd123",返回"321dcba"
        public static string Reverse(this string value)
        {
            char[] input = value.ToCharArray();
            char[] output = new char[value.Length];
            for (int i = 0; i < input.Length; i++)
                output[input.Length - 1 - i] = input[i];
            return new string(output);
        }

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

c#扩展方法奇思妙用高级篇二:Aggregate扩展其改进

编缉推荐阅读以下文章

  • c#扩展方法奇思妙用高级篇三:Enumerable.Cast应用
  • c#扩展方法奇思妙用高级篇一:改进 Scottgu 的 "In" 扩展


相关教程