Linq聚合操作之Aggregate,Count,Sum,Distinct源码分析

Linq聚合操作之Aggregate,Count,Sum,Distinct源码分析

一:Linq的聚合运算

1. 常见的聚合运算:Aggregate,Count, Sum, Distinct,Max,Min

这几个聚合运算,我们在sql中看的还是比较多的。

二:Count

1. 这个我们用到的非常多,Count() / LongCount().

2. LongCount每次都是foreach循环,所以这个性能问题就出来了。

三:Sum

var nums = new int[] { 10, 20, 30, 40, 50, 60 };

var query = nums.Sum(i => (long)i);

我们最好看一下源代码,这样方便我们了解代码的性能问题,这样可以做到心中有数。

public static decimal Sum(this IEnumerable<decimal> source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
decimal num = 0m;
foreach (decimal current in source)
{
num += current;
}
return num;
}

可以看到,其实就是一个简单的foreach。

四:Distinct

我想大家有非常清楚,它是一个去重。

static void Main(string[] args)
{
var nums = new int[] { 10, 20, 10, 40, 20, 30 };

var query = nums.Distinct();
}

源码探究:

private static IEnumerable<TSource> DistinctIterator<TSource>(IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
{
Enumerable.<DistinctIterator>d__63<TSource> expr_07 = new Enumerable.<DistinctIterator>d__63<TSource>(-2);
expr_07.<>3__source = source;
expr_07.<>3__comparer = comparer;
return expr_07;
}

一定要清楚,又是一个枚举类【<DistinctIterator>d__63`1】

可以看到,所谓的去重,就是将集合的数字添加到System.Linq.Set集合中,这个Set一看就是HashSet,对不对。。。

IL_005b: callvirt instance bool class System.Linq.Set`1<!TSource>::Add(!0)

五:Aggregate

相对来说是最复杂的,它就是一个累加器,就是对集合中的每一次数组执行一个func函数,其中有一个累加值,有一个当前值。

我们可以用Aggregate做一个Sum的操作。

static void Main(string[] args)
{
var nums = new int[] { 10, 20, 10, 40, 20, 30 };

var list = nums.Aggregate((total, next) =>
{
return total + next;
});
}

Aggregate的源码分析:

public static TSource Aggregate<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, TSource> func)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
if (func == null)
{
throw Error.ArgumentNull("func");
}
TSource result;
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
if (!enumerator.MoveNext()) //获取第一个current值。
{
throw Error.NoElements();
}
TSource tSource = enumerator.Current; //将第一个值作为全局变量。
while (enumerator.MoveNext()) //取当前的source的第二个值。
{
tSource = func(tSource, enumerator.Current); //然后将第一个值和第二个值调用func委托。
//取得的值放到全局变量tSource中。
}
result = tSource;
}
return result;
}

六: Max,Min 函数

学过sql都明白。

上一篇:【并发编程】MESI--CPU缓存一致性协议


下一篇:有趣的数-回文数(Palindrome number)