Enumerable.Aggregate() 是 Linq 的扩展方法,用于对集合序列进行累计操作。第一次使用时,可能有点摸不着头脑。
var ints = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

var result = ints.Aggregate((total, next) =>
{
  Console.WriteLine("total:{0}, next:{1}", total, next);
  return total + next;
});

Console.WriteLine(result);

输出:
total:1, next:2
total:3, next:3
total:6, next:4
total:10, next:5
total:15, next:6
total:21, next:7
total:28, next:8
total:36, next:9
45
public static TSource Aggregate<TSource>(
  this IEnumerable<TSource> source,
  Func<TSource, TSource, TSource> func
)

反编译一下,很容易看明白。
public static TSource Aggregate<TSource>(this IEnumerable<TSource> source,
  Func<TSource, TSource, TSource> func)
{
  // ... 省略部分代码 ...

  using (IEnumerator<TSource> enumerator = source.GetEnumerator())
  {
    if (!enumerator.MoveNext())
    {
      throw Error.NoElements();
    }

    TSource current = enumerator.Current;

    while (enumerator.MoveNext())
    {
      current = func(current, enumerator.Current);
    }

    return current;
  }
}

获取迭代器,提取第一个元素作为初始化累计结果,然后循环遍历其他元素,并依次调用 func 委托进行累计操作。func 有两个参数,第一个参数为上一次 func 的执行结果,第二个参数为要处理的集合元素。当然,我们并不一定要完成统计操作,也可以返回某单次 func 执行结果。

演示: 找回最长的字符串
var strs = new[] { "a", "bb", "cccc", "ddd" };

var result = strs.Aggregate((s, next) =>
{
  return next.Length > s.Length ? next : s;
});

Console.WriteLine("Max: {0}", result);

输出:
Max: cccc

除此之外,我们还可以给定一个初始值 (Seed)。
public static TAccumulate Aggregate<TSource, TAccumulate>(
  this IEnumerable<TSource> source,
  TAccumulate seed,
  Func<TAccumulate, TSource, TAccumulate> func
)

和上面不同的是,这个重载方法将 seed 作为初始化累计结果。
public static TAccumulate Aggregate<TSource, TAccumulate>(this IEnumerable<TSource> source,
  TAccumulate seed,
  Func<TAccumulate, TSource, TAccumulate> func)
{
  // ... 省略部分代码 ...

  TAccumulate local = seed;

  foreach (TSource local2 in source)
  {
    local = func(local, local2);
  }

  return local;
}

我们修改一下上面的例子,查找最大的字符串长度。我们提供了一个初始化 seed,也就是说第一次传递给 func 的 maxLength = 3。
var strs = new[] { "a", "bb", "cccc", "ddd" };

var result = strs.Aggregate(3, (maxLength, next) =>
{
  Console.WriteLine("maxLength:{0}, next:{1}", maxLength, next);
  return next.Length > maxLength ? next.Length : maxLength;
});

Console.WriteLine("Max: {0}", result);

输出:
maxLength:3, next:a
maxLength:3, next:bb
maxLength:3, next:cccc
maxLength:4, next:ddd
Max: 4

另外一个重载,可以对累计结果做出更多的处理。
public static TResult Aggregate<TSource, TAccumulate, TResult>(
  this IEnumerable<TSource> source,
  TAccumulate seed,
  Func<TAccumulate, TSource, TAccumulate> func,
  Func<TAccumulate, TResult> resultSelector
)

无非是在返回结果以前多调用一个委托而已。

public static TResult Aggregate<TSource, TAccumulate, TResult>(this IEnumerable<TSource> source,
  TAccumulate seed,
  Func<TAccumulate, TSource, TAccumulate> func,
  Func<TAccumulate, TResult> resultSelector)
{
  // ... 省略部分代码 ...

  TAccumulate local = seed;

  foreach (TSource local2 in source)
  {
    local = func(local, local2);
  }

  return resultSelector(local);
}

Func resultSelector 接收 func 的最后执行结果,执行一些特定逻辑后返回修正后的结果。

演示: 找出最长的字符串,并返回其大写结果。
var strs = new[] { "a", "bb", "cccc", "ddd" };

var result = strs.Aggregate("haa",
  (maxStr, next) =>
  {
    Console.WriteLine("maxStr:{0}, next:{1}", maxStr, next);
    return next.Length > maxStr.Length ? next : maxStr;
  },
  (maxStr) =>
  {
    return maxStr.ToUpper();
  });

Console.WriteLine("Max: {0}", result);

输出:
maxStr:haa, next:a
maxStr:haa, next:bb
maxStr:haa, next:cccc
maxStr:cccc, next:ddd
Max: CCCC

Aggregate 可以用来执行一个存储了算法委托集合的算法链。
var algorithms = new Func<string, string>[]
{
   s => s + "1",
   s => s + "2",
   s => s + "3!",
   s => s + "4",
};

var result = algorithms.Aggregate("Hello, ", (s, a) =>
{
  if (s.EndsWith("!")) return s;
  return a(s);
});

Console.WriteLine(result);

输出:
Hello, 123!
Posted on 2009-11-28 09:43  轻木剑  阅读(493)  评论(0编辑  收藏  举报