BT

如何利用碎片时间提升技术认知与能力? 点击获取答案

VB和C#中的LINQ聚合

| 作者 Jonathan Allen 关注 595 他的粉丝 ,译者 霍泰稳 关注 1 他的粉丝 发布于 2007年9月19日. 估计阅读时间: 7 分钟 | QCon上海2018 关注大数据平台技术选型、搭建、系统迁移和优化的经验。

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

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

var totalVirtualMemory =
(from p in Process.GetProcesses()

select p.VirtualMemorySize64).Sum();



Dim totalVirtualMemory = _
(From p In Process.GetProcesses _
Select p.VirtualMemorySize64).Sum

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

Dim totalVirtualMemory = Aggregate p In Process.GetProcesses _

Into p.VirtualMemorySize64

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

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

var totals = new
{
totalVirtualMemory = (from p in Process.GetProcesses()

select p.VirtualMemorySize64).Sum(),
totalWorkingSet = (from p in Process.GetProcesses()

select p.WorkingSet64).Sum()
};

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

var processes = (from p in Process.GetProcesses()
select new { p.VirtualMemorySize64, p.WorkingSet64 }

).ToList();


var totals2 = new
{
totalVirtualMemory = (from p in processes
select p.VirtualMemorySize64).Sum(),
totalWorkingSet = (from p in processes
select p.WorkingSet64).Sum()
};

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

public static ProcessTotals Sum(this IEnumerable source)
{
var totals = new ProcessTotals();
foreach (var p in source){
totals.VirtualMemorySize64 += p.VirtualMemorySize64;
totals.WorkingSet64 += p.WorkingSet64;
}
return totals;
}

public class ProcessTotals
{
public long VirtualMemorySize64 { get; set; }
public long WorkingSet64 { get; set; }
}

var totals3 = (from p in Process.GetProcesses() select p).Sum();

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

Dim totals3 = Aggregate p In Process.GetProcesses _
Into virtualMemory = Sum(p.VirtualMemorySize64), _
workingSet = Sum(p.WorkingSet64)

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

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

var processes =
(from p in Process.GetProcesses()
select new { p.VirtualMemorySize64, p.WorkingSet64 });
var totals4 = processes.Aggregate(new ProcessTotals(), (sum, p) =>
{
sum.WorkingSet64 += p.WorkingSet64;
sum.VirtualMemorySize64 += p.VirtualMemorySize64;
return sum;

});

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

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

查看英文原文:LINQ Aggregates in VB and C#

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

题外话 by Wang Chunshan

其余的基本mini书什么时候发布中文版啊,好象上次是说9月中旬:)

Re: 题外话 by 霍 泰稳

“基本”应该是“几本”:)
这次好像有点拖后,但就在这几天了会再发布一本的,稍安勿燥,呵呵。

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

2 讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT