c#

位置:IT落伍者 >> c# >> 浏览文章

Visual Basic和C#中的LINQ聚合


发布日期:2019年02月01日
 
Visual Basic和C#中的LINQ聚合

Aggregate是一个可以从一个数据集合中获取标量值的函数比如TSQL中的Min()Max()和Sum()等现在VB和C#也都对这种聚合的功能给于了支持但是是以一种非常不同的方式

VB和C#都是以扩展方法的形式支持聚合的在一个IEnumberable对象中一个简单的调用是通过点符号完成的比如

var totalVirtualMemory = (from p in ProcessGetProcesses()

select pVirtualMemorySize)Sum();

Dim totalVirtualMemory = _

(From p In ProcessGetProcesses _

Select pVirtualMemorySize)Sum

从这儿可以看到VB和C#的版本几乎是一样的但VB还为聚合专门提供了一个LINQ语法

Dim totalVirtualMemory = Aggregate p In ProcessGetProcesses _ Into pVirtualMemorySize

如果这是二者之间唯一区别的话那么也就没有什么好谈的了但是有趣的事情发生在当你想同时操作不止一个的时候简便起见我们假设要操作正在使用的全部虚拟内存和全部工作集(物理内存)

使用匿名类我们可以轻松地创建一个带有它们两个值的变量

var totals = new {

totalVirtualMemory = (from p in ProcessGetProcesses() select pVirtualMemorySize)Sum()

totalWorkingSet = (from p in ProcessGetProcesses() select pWorkingSet)Sum() };

这儿的问题是GetProcesses()被调用了两次也就是说操作系统必须查询两次在结果集合中执行两次循环一个更快的方法也许是对GetProcesses()的调用进行缓存

var processes = (from p in ProcessGetProcesses() select new { pVirtualMemorySize pWorkingSet } )ToList();

var totals = new { totalVirtualMemory = (from p in processes select pVirtualMemorySize)Sum()

totalWorkingSet = (from p in processes select pWorkingSet)Sum()

};

虽然好了一些但仍然需要两次循环如何只执行一次呢?这时我们需要一个定制的聚合器和一个保存这些结果的命名类(Named Class)

public static ProcessTotals Sum(this IEnumerable source) {

var totals = new ProcessTotals();

foreach (var p in source){

totalsVirtualMemorySize += pVirtualMemorySize;

totalsWorkingSet += pWorkingSet;

}

return totals;

}

public class ProcessTotals {

public long VirtualMemorySize { get; set; }

public long WorkingSet { get; set; }

}

var totals = (from p in ProcessGetProcesses() select p)Sum();

开发者在Visual Basic中也可以这样做但需要像下面这样做

Dim totals = Aggregate p In ProcessGetProcesses _

Into virtualMemory = Sum(pVirtualMemorySize) _

workingSet = Sum(pWorkingSet)

就像在上一个C#例子中我们是用一个含有两个Field的变量解决问题的但这和C#的例子不一样你不会因为是选择创建自己的聚合函数及类还是在遍历集合中浪费两次循环而左右为难

公平起见C#确实还有那么几招不像VB那样只支持单行的匿名函数只要需要C#可以让它们很复杂这就使得它可以在需要的时候创建匿名的聚合函数

var processes = (from p in ProcessGetProcesses() select new { pVirtualMemorySize pWorkingSet });

var totals = processesAggregate(new ProcessTotals() (sum p) => { sumWorkingSet += pWorkingSet;

sumVirtualMemorySize += pVirtualMemorySize; return sum;

});

注意在这儿ProcessTotals类依然需要用到匿名类不能被用在这儿因为C#匿名类是不可变的虽然Visual Basic的匿名类可以改变但是在这儿也没用因为VB不能创建多行的匿名函数

但是Visual Basic和C#都较从前有了强有力的改进双方也各有长处让对方在不足之处加油赶上

上一篇:了解C#特性匿名类型与隐式类型局部变量

下一篇:C#在winform中查找控件