当前位置: 首页 > 编程日记 > 正文

7. Query Expressions(查询表达式)

【返回目录】

查询表达式提供了与SQL这样的关系化和分级的查询语言相类似的语言集成的语法。一个查询表达式是以from子句开头以select或者group子句结束,这个初始的from子句可以在其后跟随任意多个from、let、where或者join子句。

那么查询表达式中的这些子句都是做什么的呢?from子句是一个引入了一个覆盖整个序列的范围变量的生成器;let子句计算一个值并传入一个标识符来显示这个值;where子句是一个从结果集中排除元素的过滤器;join子句用于比对源序列和另一个序列中指定的键并找出匹配的对;select或group子句根据范围变量来指定结果的形式;into子句可以将一个查询的结果作为后续查询中的生成器并以此来拼接两个查询。

前面提到了每一条查询语句都必须是以from开头的,在from后面可以接除“;”、“,”和“=”以外所有的符号,如果要在查询语句中使用这些标识符,需要在前面加上“@”符号。

C# 3.0并没有给查询表达式指定明确的执行语义,准确地说,C# 3.0把查询语句翻译为对依附于查询表达式模式的方法的调用,这些方法可以是对象实例方法也可以是扩展方法,它们执行的才是实际的查询工作。查询表达式翻译为方法调用的过程是发生在类型绑定或者过载抉择之前的句法映射过程,翻译可以保证语法正确但不能确保生成的一定是语法正确的C#代码。在翻译之后,作为结果的方法调用会被作为一般的方法调用,并且这可能会依次找出错误,例如方法不存在、参数中存在错误的类型或者对范型方法的类型推断失败之类的。

一条查询语句的执行是以重复地应用后续翻译直到不再有更深一层可进行的缩减。这些翻译根据优先权排列,每个部分都假定前一部分中的翻译已经被完全地执行。明确的翻译采用透明标志符(Transparent identifiers)“*”来注入范围变量。

接下来我们还是来看码说话吧,这样可能更便于理解和掌握。前面提到,查询表达式并不具备确切的执行语义,而是用来被翻译成采用查询表达式模式的方法调用,那我们就来看看这里所说的翻译到底是什么样子的吧。

   1: public static void Query()
   2: {
   3:     Dictionary<string, int> students = new Dictionary<string, int>();
   4:     students.Add("ZeroCool", 24);
   5:     students.Add("Michael", 21);
   6:     students.Add("Frieda", 22);
   7:  
   8:     IEnumerable<KeyValuePair<string, int>> queryResult = from student in students
   9:                                                          where student.Value <= 23
  10:                                                          orderby student.Value descending
  11:                                                          select student;
  12:  
  13:     foreach (var student in queryResult)
  14:     {
  15:         Console.WriteLine(student.Key + " is " + student.Value + " years old.");
  16:     }
  17:  
  18:     Console.ReadLine();
  19: }

上面这段代码要做的事情是从students这个集合里面找出所有年龄小于24岁的学生并按年龄降序排序,我们着重来看一下第8到第11行代码。我想所有写过SQL查询语句的朋友都会对这句话感到似曾相识。是的,它非常像SQL查询语句,但又略有差别,我们来看一下这句话会被C# 3.0翻译成怎样的方法就会了解它是如何工作的了。

   1: IEnumerable<KeyValuePair<string, int>> queryResult
   2:     = students.Where(student => student.Value <= 23).OrderByDescending(student => student.Value).Select(student => student);

上面这行代码就是查询语句的翻译结果,这行代码放在原先程序中替换掉第8到第11行代码依然是可以得出同样的结果的,或许这看上去还不太好理解吧,那么我们用先前介绍过的表达式树来翻译。

   1: Func<KeyValuePair<string, int>, bool> filter = student => student.Value <= 23;
   2: Func<KeyValuePair<string, int>, int> extract = student => student.Value;
   3: Func<KeyValuePair<string, int>, KeyValuePair<string, int>> result = student => student;
   4:  
   5: IEnumerable<KeyValuePair<string, int>> queryResult = students.Where(filter).OrderByDescending(extract).Select(result);

这样,虽然代码量多了一点,但的确理解起来方便了很多。翻译的结果是直接调用原数据集合对象的Where方法选出符合条件的候选项,然后在用OrderByDescending方法进行降序排序,最后通过Select方法把这些结果返回回来。上面的例子虽然比较简单,但是可以让我们很好地了解查询表达式的语法以及翻译的方式,只要我们了解了最基本也是最核心的概念,那么即使面对复杂的查询表达式也可以迎刃而解了。

前面提到过一个概念叫做“透明标识符”,可能其定义比较抽象大家不太好理解,现在我们借助一个翻译过程来解释一下到底什么事透明标识符吧。

   1: from customer in customers
   2: from order in customer.Orders
   3: orderby order.Total descending
   4: select new { customer.Name, order.Total };

我们假设有上面这句查询语句,它的工作是从消费者的集合里找出每个消费者的名字和他的订购总量,并按照每个消费者订购总量对他们进行降序排序。接下来我们来看看这句查询表达式将会被翻译成什么样子把。

   1: from * in
   2:     from customer in customers
   3:     from order in customer.Orders
   4:     select new { customer, order }
   5: orderby order.Total descending
   6: select new { customer.Name, order.Total }

现在透明标识符*已经出现了,不过这还只是中间步骤,我们继续往下翻译:

   1: customers.
   2: SelectMany(customer => customer.Orders.Select(order => new { customer, order })).
   3: OrderByDescending(* => order.Total).Select(* => new { customer.Name, order.Total })

如果我们把透明标识符去掉,那么上面几行代码就等效于:

   1: customers.
   2: SelectMany(customer => customer.Orders.Select(o => new { customer, order })).
   3: OrderByDescending(x => x.order.Total).Select(x => new { x.customer.Name, x.order.Total })

其中的x是编译器生成的一个开发人员看不见摸不着的标识符。透明标识符不是一个确切的语言特性,它只作为查询表达式翻译过程中的一个中间步骤。

当查询表达式注入一个透明标识符时,接下来的翻译步骤会将这个透明标识符传入到Lambda表达式和匿名对象初始器中去。在这样的上下文中,透明标识符具有以下的行为:

  • 当一个透明标识符作为Lambda表达式中的参数出现的时候,相关联的匿名类型的成员将自动处于Lambda表达式体中的范围内;
  • 当一个含有透明标识符的成员处于当前范围内,那么这个成员的成员也都处于当前范围内;
  • 当一个透明标识符作为一个匿名对象初始器中的成员声明符出现时,它会引入一个带有透明标识符的成员;

好了,查询表达式就介绍到这里吧,也许你觉得查询表达式有很多概念读起来比较抽象,不那么便于理解,但是我相信,只要你动手多练习一下,很多概念都能很好地理解的。《C# 3.0 探索之旅》系列技术介绍文章也就到此为止了,我相信C# 3.0为我们广大开发人员带来的这些新特性都能够是我们在很大程度上提高开发效率,同时也增加了很多开发的乐趣,随着微软不断有面向各个层面和领域的新技术问世,我相信.NET平台也会逐渐成为一个更加广泛、强大的技术平台。接下来的时间我会和大家一起共同探讨一下微软的另一个重头戏——LINQ项目,敬请关注!

转载于:https://www.cnblogs.com/Autumoon/archive/2007/11/21/967191.html

相关文章:

CSS完美兼容IE6/IE7/IE8/IE9/IE10的通用方法

300px!important;width /**/:340px;margin:0 10px 0 10px} &#xff0c;关于这个/**/是什么我也不太明白&#xff0c;只知道IE5和firefox都支持但IE6不支持&#xff0c;如果有人理解的话&#xff0c;请告诉我一声&#xff0c;谢了&#xff01;&#xff1a;&#xff09; 3、ul标…

(C++)A+B 输入输出练习VII 输入包含若干行,每行输入两个整数a和b,由空格分隔。 对于每组输入,输出a和b的和,每行输出后接一个空行。

#include<stdio.h> /* 1 5 10 20 */int main() { int a,b;while(scanf("%d%d",&a,&b) ! EOF){printf("%d\n\n",ab);}return 0;}

Address already in use: JVM_Bind错误的解决

错误原因 tomcat的8005端口号被占用了 解决办法 关闭已有的占用端口 1. cmd—>netstat -an 查看当前开启的端口号 2. netstat -ano 获得端口号的pid码 3. skill -{pid} 杀死端口进程转载于:https://www.cnblogs.com/lxq0309/p/3736899.html

在SQL Server中如何转化长日期形式为短日期格式

convert(nvarchar(10),字段名,121)即可将时间格式转化为yyyy-mm-dd格式 convert中的121是指将datetime类型转换为char类型时获得包括世纪位数的4位年份。转载于:https://www.cnblogs.com/footleg/archive/2007/11/29/976451.html

看看Vector源码Java 9

2019独角兽企业重金招聘Python工程师标准>>> Vector类实现了一个可增长的对象数组。像数组一样&#xff0c;它包含可以使用整数索引随机访问。但是&#xff0c;Vector的大小可以根据需要增大或缩小&#xff0c;以适应在创建Vector之后添加和删除项目。 文档里的内容…

(C++)1016 部分A+B 正整数

#include<cstdio>int main(){ //1.读入a,Da,b,Dblong long a,b,Pa0,Pb0;int Da,Db;scanf("%lld%d%lld%d",&a,&Da,&b,&Db); //2.对于a,遍历每一位&#xff0c;加在Pa上 //2.1取余的方式遍历while(a>0){if(a%10Da){Pa Pa*10 Da;}a a/10;} …

MySQL Innodb日志机制深入分析

1.1. Log & Checkpoint Innodb的事务日志是指Redo log&#xff0c;简称Log,保存在日志文件ib_logfile*里面。Innodb还有另外一个日志Undo log&#xff0c;但Undo log是存放在共享表空间里面的&#xff08;ibdata*文件&#xff09;。 由于Log和Checkpoint紧密相关&#xff0…

单元测试的重要性

一些错误的认识 在实际的单元测试过程中总会有一些错误的认识左右着我们&#xff0c;使之成为单元测试最大的障碍&#xff0c;在此将其一一分析如下&#xff1a; 它太浪费时间了&#xff0c;现在要赶进度&#xff0c;时间上根本不允许&#xff0c;或者随便做做应付领导。 我是一…

浅谈网络协议(四) IP的由来--DHCP与PXE

2019独角兽企业重金招聘Python工程师标准>>> 上一节说过&#xff0c;IP就是一台计算机的通讯地址&#xff0c;要和其他机器通讯&#xff0c;就需要一个通讯地址&#xff0c;就要给网卡配置这么一个地址。 配置 IP 那如何配置呢&#xff1f;可以使用 ifconfig&#x…

(C++)1026 程序运行时间

#include<cstdio> const int CLK_TCK100;int main(){ //1.读入c1,c2int c1,c2;scanf("%d%d",&c1,&c2); //2.定义常量CLK_TCK100 //难点&#xff1a;不足 1 秒的时间四舍五入到秒 --不用round()&#xff0c;避免浮点数运算 int dif c2-c1;if(dif%100&…

Spring中@Autowired注解、@Resource注解的区别

Spring不但支持自己定义的Autowired注解&#xff0c;还支持几个由JSR-250规范定义的注解&#xff0c;它们分别是Resource、PostConstruct以及PreDestroy。  Resource的作用相当于Autowired&#xff0c;只不过Autowired按byType自动注入&#xff0c;而Resource默认按 byName自…

(C++)1046 划拳

划拳是古老中国酒文化的一个有趣的组成部分。酒桌上两人划拳的方法为&#xff1a;每人口中喊出一个数字&#xff0c;同时用手比划出一个数字。如果谁比划出的数字正好等于两人喊出的数字之和&#xff0c;谁就赢了&#xff0c;输家罚一杯酒。两人同赢或两人同输则继续下一轮&…

JAVA中重写equals()方法的同时要重写hashcode()方法

object对象中的 public boolean equals(Object obj)&#xff0c;对于任何非空引用值 x 和 y&#xff0c;当且仅当 x 和 y 引用同一个对象时&#xff0c;此方法才返回 true&#xff1b;注意&#xff1a;当此方法被重写时&#xff0c;通常有必要重写 hashCode 方法&#xff0c;以…

从0到1,苏宁API网关的演进之路

http://www.infoq.com/cn/articles/suning-11-11-api-gateway?utm_campaigninfoq_content&utm_sourceinfoq&utm_mediumfeed&utm_termglobal 2012年&#xff0c;在开放云融推动各产业全面发展的大背景下&#xff0c;苏宁API对外开放。基于苏宁各内部业务系统的资源…

HTML 注意事项

这行与下面图片的间距比较小asdf 代码如下: <table id"table1"cellspacing"0"cellpadding"0"width"100%"border"0"><tbody><tr><td>这行与下面图片的间距比较小</td></tr><tr>&l…

(C++)1008 数组元素循环右移问题

#include<cstdio> //注意&#xff1a;不允许使用另外数组,序列结尾不能有多余空格,不能直接认为right<n //1.读入数组长度&#xff0c;和右移位数&#xff0c;读入数组 //2.未必要对实际数组进行循环右移&#xff0c;只要输出结果表现出那样就可以 int main(){int n…

C# 文件操作(上传 下载 删除 文件列表...)

using System.IO; 1.文件上传 ---------- 如下要点&#xff1a; HTML部分&#xff1a; <form id"form1" runat"server" method"post" enctype"multipart/form-data"> <input id"FileUpLoad" type"file" …

5个常用Java代码混淆器 助你保护你的代码

【IT168 技术文档】 从事Java编程的人都知道&#xff0c;可以通过逆向工程反编译得到Java程序的源代码&#xff0c;这种反编译工具之一就是JAD。因此&#xff0c;为保护我们的劳动成果&#xff0c;尽可能给反编译人员制造障碍&#xff0c;我们可以使用Java Obfuscator(Java混淆…

9.8.6恢复系统数据库

系统数据库和用户数据库一样容易发生故障&#xff0c;所以确保它们得到足够的保护十分重 要。一般来说&#xff0c;恢复系统数据库时有两种选择。可以从备份还原&#xff0c;或者从头重建它们。我 强烈推荐备份还原方法&#xff0c;因为从头重建意味着有人量的工作要做。由于系…

(C++)1012 数字分类

#include<cstdio> //用switch...case语句来对读入的数字进行分类 //1.读入N //2.A2需要设置变号器 //3.A3,A4需要设置计数器 //注意&#xff1a;某一类数可能根本不存在 int main(){int n;scanf("%d",&n);int res[5]{};//初始化为0 int count[5]{};whil…

老调重弹:插件式框架开发的一个简单应用

VS 2008最近要做一个应用程序检测程序&#xff0c;就是要检测服务器上各应用程序的运行情况&#xff0c;由于各应用程序的差异&#xff0c;很难做一个统一的探测程序&#xff0c;于是决定对任意一个应用程序都采用独立的一条探测规则。为了开发、部署的方便&#xff0c;考虑使用…

mathjax测试

O(∩_∩)O哈哈~&#xff0c;新开通博客测试。 mathjax公式测试。以下是latex公式 $a^2b^2c^2$ <!--more--> 多输入一点东东。新浪娱乐讯 1月3日&#xff0c;网络红人奶茶妹妹章泽天清空微博&#xff0c;而其男友刘强东也删除了“小天是我见过最单纯善良的人…只求以后可以…

Docker镜像使用

当运行容器时&#xff0c;使用的镜像如果在本地中不存在&#xff0c;docker 就会自动从 docker 镜像仓库中下载&#xff0c;默认是从 Docker Hub 公共镜像源下载。 下面我们来学习&#xff1a; 1、管理和使用本地 Docker 主机镜像2、创建镜像列出镜像列表 我们可以使用 docker …

(C++)1018 锤子剪刀布

#include<cstdio>int Map(char c){//将字母映射到数字 if(cB){return 0;}else if(cC){return 1;}else{return 2;} } //对决函数&#xff0c;返回1表示甲胜&#xff0c;0表示平局&#xff0c;-1表示乙胜 int ko(int i1,int i2){if((i11)%3i2){//甲胜 return 1;}else if(i…

我的软考之路(九)——总结篇

经过两个月的备战&#xff0c;软考总算结束了。软考虽然结束了&#xff0c;但是还需要简单的总结一下得与失。我从时间安排&#xff0c;到讲课做真题简单的回顾一下软考的整个过程。 时间安排&#xff1a; 对于时间的安排&#xff0c;整个小组成员每个人都有自己的看法&#xf…

problem-solving-with-algorithms-and-data-structure-usingpython(使用python解决算法和数据结构) -- 基本数据结构(二)...

中缀、前缀和后缀表达式 1. 前缀表达式符号要求所有运算符在它们处理的两个操作数之前。 2. 后缀表达式要求其操作符在相应的操作数之后。 考虑表达式 A B * C &#xff0c; A B C * 是等价的后缀表达式。操作数 A&#xff0c;B 和 C 保持在它们的相对位置&#xff0c;只有操…

赢在中国(08-02-27)

在Google Calendar中设置了赢在中国的日程&#xff0c;结果没有发短信给我&#xff08;是不是可以起诉它呢&#xff1f;和Google打打官司&#xff0c;可是成名的好机会啊&#xff1a;&#xff09;&#xff09;后面一节还是看到了&#xff0c;印象比较深的是最后一位做教育的选手…

(C++)1041 考试座位号

//B1041 #include<cstdio> //直接将试机号作为下标 struct student{long long no;int sit; };int main(){int n1,n2;//总共参与考试的人数&#xff0c;来迟的人数 student stus[1010];scanf("%d",&n1);long long no;int sit,test;for(int i0;i<n1;i){s…

Premiere制作VCD视频几个关键设置

一&#xff0c;视频设置 1&#xff0c;编码设置&#xff1a;不要选cinepak codec by radius,这个编码速度非常慢,图象也不清晰,一般只在电脑多多媒体交互式演示文件中使用。很多朋友遇到的“速度慢”、“不清晰”&#xff0c;多半是这里设置不合适造成。 2&#xff0c;帧尺寸&a…

qt中定时器Timer的使用

转载于:https://www.cnblogs.com/zhouwenJS/p/3762341.html