C#3.0新增功能07 查询表达式
查询是一组指令,描述要从给定数据源(或源)检索的数据以及返回的数据应具有的形状和组织。 查询与它生成的结果不同。
通常情况下,源数据按逻辑方式组织为相同类型的元素的序列。 例如,SQL 数据库表包含行的序列。 在 XML 文件中,存在 XML 元素的“序列”(尽管这些元素在树结构按层次结构进行组织)。 内存中集合包含对象的序列。
从应用程序的角度来看,原始源数据的特定类型和结构并不重要。 应用程序始终将源数据视为 IEnumerable<T> 或 IQueryable<T> 集合。 例如在 LINQ to XML 中,源数据显示为 IEnumerable
<XElement>。
对于此源序列,查询可能会执行三种操作之一:
检索元素的子集以生成新序列,而不修改各个元素。 查询然后可能以各种方式对返回的序列进行排序或分组,如下面的示例所示(假定
scores
是int[]
):
IEnumerable<int> highScoresQuery =
from score in scores
where score >
orderby score descending
select score;
- 如前面的示例所示检索元素的序列,但是将它们转换为新类型的对象。 例如,查询可以只从数据源中的某些客户记录检索姓氏。 或者可以检索完整记录,然后用于构造其他内存中对象类型甚至是 XML 数据,再生成最终的结果序列。 下面的示例演示从
int
到string
的投影。 请注意highScoresQuery
的新类型。
IEnumerable<string> highScoresQuery2 =
from score in scores
where score >
orderby score descending
select $"The score is {score}";
检索有关源数据的单独值,如:
与特定条件匹配的元素数。
具有最大或最小值的元素。
与某个条件匹配的第一个元素,或指定元素集中特定值的总和。 例如,下面的查询从
scores
整数数组返回大于 80 的分数的数量:
int highScoreCount =
(from score in scores
where score >
select score)
.Count();
在前面的示例中,请注意在调用 Count
方法之前,在查询表达式两边使用了括号。也可以通过使用新变量存储具体结果,来表示此行为。 这种方法更具可读性,因为它使存储查询的变量与存储结果的查询分开。
IEnumerable<int> highScoresQuery3 =
from score in scores
where score >
select score; int scoreCount = highScoresQuery3.Count();
在上面的示例中,查询在 Count
调用中执行,因为 Count
必须循环访问结果才能确定 highScoresQuery
返回的元素数。
from
子句与最后一个 select
或 group
子句之间,可以包含以下这些可选子句中的一个或多个:where、orderby、join、let,甚至是其他 from 子句。 还可以使用 into 关键字,使 join
或 group
子句的结果可以充当相同查询表达式中的其他查询子句的源。查询变量
在 LINQ 中,查询变量是存储查询而不是查询结果的任何变量。 更具体地说,查询变量始终是可枚举类型,在 foreach
语句或对其 IEnumerator.MoveNext
方法的直接调用中循环访问时会生成元素序列。
下面的代码示例演示一个简单查询表达式,它具有一个数据源、一个筛选子句、一个排序子句并且不转换源元素。 该查询以 select
子句结尾。
static void Main()
{
// 数据源
int[] scores = { , , , , , }; // 查询表达式
IEnumerable<int> scoreQuery = // 查询变量
from score in scores // 必须
where score > 80 // 可选
orderby score descending // 可选
select score; // 必须以 select 或者 group 结尾 // 执行查询并产生结果
foreach (int testScore in scoreQuery)
{
Console.WriteLine(testScore);
}
}
// 输出: 93 90 82 82
在上面的示例中,scoreQuery
是查询变量,它有时仅仅称为查询。 查询变量不存储在 foreach
循环生成中的任何实际结果数据。 并且当 foreach
语句执行时,查询结果不会通过查询变量 scoreQuery
返回。 而是通过迭代变量 testScore
返回。 scoreQuery
变量可以在另一个 foreach
循环中进行循环访问。 只要既没有修改它,也没有修改数据源,便会生成相同结果。
查询变量可以存储采用查询语法、方法语法或是两者的组合进行表示的查询。 在以下示例中,queryMajorCities
和 queryMajorCities2
都是查询变量:
var cities = new City
{
new city(){Name = "上海",Population = },
new city(){Name = "南京",Population = },
new city(){Name = "北京",Population = },
new city(){Name = "广州",Population = }
}; // 查询语法
IEnumerable<City> queryMajorCities =
from city in cities
where city.Population >
select city; // 基于方法的语法
IEnumerable<City> queryMajorCities2 = cities.Where(c => c.Population > );
另一方面,以下两个示例演示不是查询变量的变量(即使各自使用查询进行初始化)。 它们不是查询变量,因为它们存储结果:
int highestScore =
(from score in scores
select score)
.Max(); // 或者拆分表达式
IEnumerable<int> scoreQuery =
from score in scores
select score; int highScore = scoreQuery.Max();
// 下面的表达式返回相同的结果
int highScore = scores.Max(); List<City> largeCitiesList =
(from country in countries
from city in country.Cities
where city.Population >
select city)
.ToList(); // 或者拆分表达式
IEnumerable<City> largeCitiesQuery =
from country in countries
from city in country.Cities
where city.Population >
select city; List<City> largeCitiesList2 = largeCitiesQuery.ToList();
有关表示查询的不同方式的详细信息,请参阅 LINQ 中的查询语法和方法语法。
查询变量的显式和隐式类型化
本文档通常提供查询变量的显式类型以便显示查询变量与 select 子句之间的类型关系。 但是,还可以使用 var 关键字指示编译器在编译时推断查询变量(或任何其他局部变量)的类型。 例如,本主题中前面演示的查询示例也可以使用隐式类型化进行表示:
// 在这里和所有查询中使用var都是可选的。querycities是一个IEnumerable<city>就像它是显式类型一样
var queryCities =
from city in cities
where city.Population >
select city;
有关详细信息,请参阅隐式类型化局部变量和 LINQ 查询操作中的类型关系。
开始查询表达式
查询表达式必须以 from
子句开头。 它指定数据源以及范围变量。 范围变量表示遍历源序列时,源序列中的每个连续元素。 范围变量基于数据源中元素的类型进行强类型化。 在下面的示例中,因为 countries
是 Country
对象的数组,所以范围变量也类型化为 Country
。 因为范围变量是强类型,所以可以使用点运算符访问该类型的任何可用成员。
IEnumerable<Country> countryAreaQuery =
from country in countries
where country.Area > //面积大于500000
select country;
范围变量一直处于范围中,直到查询使用分号或 continuation 子句退出。
查询表达式可能会包含多个 from
子句。 在源序列中的每个元素本身是集合或包含集合时,可使用其他 from
子句。 例如,假设具有 Country
对象的集合,其中每个对象都包含名为 Cities
的 City
对象集合。 若要查询每个 Country
中的 City
对象,请使用两个 from
子句,如下所示:
IEnumerable<City> cityQuery =
from country in countries
from city in country.Cities
where city.Population >
select city;
有关详细信息,请参阅 from 子句。
结束查询表达式
查询表达式必须以 group
子句或 select
子句结尾。
group 子句
使用 group
子句可生成按指定键组织的组的序列。 键可以是任何数据类型。 例如,下面的查询会创建包含一个或多个 Country
对象并且其键是 char
值的组的序列。
var queryCountryGroups =
from country in countries
group country by country.Name[];
有关分组的详细信息,请参阅 group 子句。
select 子句
使用 select
子句可生成所有其他类型的序列。 简单 select
子句只生成类型与数据源中包含的对象相同的对象的序列。 在此示例中,数据源包含 Country
对象。 orderby
子句只按新顺序对元素进行排序,而 select
子句生成重新排序的 Country
对象的序列。
IEnumerable<Country> sortedQuery =
from country in countries
orderby country.Area
select country;
select
子句可以用于将源数据转换为新类型的序列。 此转换也称为投影。 在下面的示例中,select
子句对只包含原始元素中的字段子集的匿名类型序列进行投影。 请注意,新对象使用对象初始值设定项进行初始化。
// 此处 var 是必须的,因为查询返回了匿名类型
var queryNameAndPop =
from country in countries
select new { Name = country.Name, Pop = country.Population };
有关可以使用 select
子句转换源数据的所有方法的详细信息,请参阅 select 子句。
使用“into”进行延续
可以在 select
或 group
子句中使用 into
关键字创建存储查询的临时标识符。 如果在分组或选择操作之后必须对查询执行其他查询操作,则可以这样做。 在下面的示例中,countries
按 1000 万范围,根据人口进行分组。 创建这些组之后,附加子句会筛选出一些组,然后按升序对组进行排序。 若要执行这些附加操作,需要由 countryGroup
表示的延续。
// 该查询返回的类型是 IEnumerable<IGrouping<int, Country>>
var percentileQuery =
from country in countries
let percentile = (int) country.Population / 10_000_000
group country by percentile into countryGroup
where countryGroup.Key >=
orderby countryGroup.Key
select countryGroup; // 分组是 IGrouping<int, Country>
foreach (var grouping in percentileQuery)
{
Console.WriteLine(grouping.Key);
foreach (var country in grouping)
Console.WriteLine(country.Name + ":" + country.Population);
}
有关详细信息,请参阅 into。
筛选、排序和联接
在开头 from
子句与结尾 select
或 group
子句之间,所有其他子句(where
、join
、orderby
、from
、let
)都是可选的。 任何可选子句都可以在查询正文中使用零次或多次。
IEnumerable<City> queryCityPop =
from city in cities
where city.Population < && city.Population >
select city;
有关详细信息,请参阅 where 子句。
orderby 子句
使用 orderby
子句可按升序或降序对结果进行排序。 还可以指定次要排序顺序。 下面的示例使用 Area
属性对 country
对象执行主要排序。 然后使用 Population
属性执行次要排序。
IEnumerable<Country> querySortedCountries =
from country in countries
orderby country.Area, country.Population descending
select country;
ascending
关键字是可选的;如果未指定任何顺序,则它是默认排序顺序。 有关详细信息,请参阅 orderby 子句。
join 子句
使用 join
子句可基于每个元素中指定的键之间的相等比较,将一个数据源中的元素与另一个数据源中的元素进行关联和/或合并。 在 LINQ 中,联接操作是对元素属于不同类型的对象序列执行。 联接了两个序列之后,必须使用 select
或 group
语句指定要存储在输出序列中的元素。 还可以使用匿名类型将每组关联元素中的属性合并到输出序列的新类型中。下面的示例关联其 Category
属性与 categories
字符串数组中一个类别匹配的 prod
对象。筛选出其 Category
不与 categories
中的任何字符串匹配的产品。select
语句会投影其属性取自 cat
和 prod
的新类型。
var categoryQuery =
from cat in categories
join prod in products on cat equals prod.Category
select new { Category = cat, Name = prod.Name };
还可以通过使用 into 关键字将 join
操作的结果存储到临时变量中来执行分组联接。 有关详细信息,请参阅 join 子句。
let 子句
使用 let
子句可将表达式(如方法调用)的结果存储在新范围变量中。 在下面的示例中,范围变量 firstName
存储 Split
返回的字符串数组的第一个元素。
string[] names = { "Svetlana Omelchenko", "Claire O'Donnell", "Sven Mortensen", "Cesar Garcia" };
IEnumerable<string> queryFirstNames =
from name in names
let firstName = name.Split(' ')[]
select firstName; foreach (string s in queryFirstNames)
Console.Write(s + " ");
//输出: Svetlana Claire Sven Cesar
有关详细信息,请参阅 let 子句。
查询表达式中的子查询
查询子句本身可能包含查询表达式,这有时称为子查询。 每个子查询都以自己的 from
子句开头,该子句不一定指向第一个 from
子句中的相同数据源。 例如,下面的查询演示在 select 语句用于检索分组操作结果的查询表达式。
var queryGroupMax =
from student in students
group student by student.GradeLevel into studentGroup
select new
{
Level = studentGroup.Key,
HighestScore =
(from student2 in studentGroup
select student2.Scores.Average())
.Max()
};
有关详细信息,请参阅如何:对分组操作执行子查询。
其他技术请参阅
C#3.0新增功能07 查询表达式的更多相关文章
- C#3.0新增功能08 Lambda 表达式
连载目录 [已更新最新开发文章,点击查看详细] Lambda 表达式是作为对象处理的代码块(表达式或语句块). 它可作为参数传递给方法,也可通过方法调用返回. Lambda 表达式广泛用于: 将 ...
- C#2.0新增功能07 getter/setter 单独可访问性
连载目录 [已更新最新开发文章,点击查看详细] 属性是一种成员,它提供灵活的机制来读取.写入或计算私有字段的值. 属性可用作公共数据成员,但它们实际上是称为访问器的特殊方法. 这使得可以轻松访问 ...
- C#基础拾遗系列之二:使用ILSpy探索C#7.0新增功能点
C#基础拾遗系列之二:使用ILSpy探索C#7.0新增功能点 第一部分: C#是一种通用的,类型安全的,面向对象的编程语言.有如下特点: (1)面向对象:c# 是面向对象的范例的一个丰富实现, 它 ...
- C#2.0新增功能06 协变和逆变
连载目录 [已更新最新开发文章,点击查看详细] 在 C# 中,协变和逆变能够实现数组类型.委托类型和泛型类型参数的隐式引用转换. 协变保留分配兼容性,逆变则与之相反. 以下代码演示分配兼容性.协 ...
- C#3.0新增功能09 LINQ 基础07 LINQ 中的查询语法和方法语法
连载目录 [已更新最新开发文章,点击查看详细] 介绍性的语言集成查询 (LINQ) 文档中的大多数查询是使用 LINQ 声明性查询语法编写的.但是在编译代码时,查询语法必须转换为针对 .NET ...
- C#3.0新增功能09 LINQ 标准查询运算符 02 查询表达式语法
连载目录 [已更新最新开发文章,点击查看详细] 某些使用更频繁的标准查询运算符具有专用的 C# 语言关键字语法,使用这些语法可以在查询表达式中调用这些运算符. 查询表达式是比基于方法的等效项更具 ...
- C#3.0新增功能09 LINQ 基础04 基本 LINQ 查询操作
连载目录 [已更新最新开发文章,点击查看详细] 本篇介绍 LINQ 查询表达式和一些在查询中执行的典型操作. 获取数据源 在 LINQ 查询中,第一步是指定数据源. 和大多数编程语言相同,在使用 ...
- C#3.0新增功能09 LINQ 标准查询运算符 01 概述
连载目录 [已更新最新开发文章,点击查看详细] 标准查询运算符 是组成 LINQ 模式的方法. 这些方法中的大多数都作用于序列:其中序列指其类型实现 IEnumerable<T> 接 ...
- C#3.0新增功能09 LINQ 标准查询运算符 04 运算
连载目录 [已更新最新开发文章,点击查看详细] 本篇主要介绍标准查询运算符的常用运算功能. 01 对数据排序 排序操作基于一个或多个属性对序列的元素进行排序. 第一个排序条件对元素执行主要排序. ...
随机推荐
- 初涉Delphi Socket编程
不是第一次接触socket编程了,但以前都是看别人的依葫芦画瓢,也不知道具体的原理. 新的项目,有了新的开始,同时也需要有新的认识. Delphi 中带有两套TCP Socket组件: Indy So ...
- DUI-Windows消息机制要点(34篇)
[隐藏] 1窗口过程概念 2消息类型 2.1系统定义消息 2.1.1窗口消息 2.1.2命令消息 2.1.3控件通知消息 2.1.4程序定义消息 3消息队列 3.1系统消息队列 3.2线程消息队列 4 ...
- Windows Phone8.1系统新特性
Windows Phone 8.1 beta SDK已经为大家透露了不少WP8.1系统的新特性,不过这些新特性还不能保证在最终的消费者版本中都有所体现,毕竟它还仅是SDK版本.日前,国外媒体WPCen ...
- shell多线程之进程间通信(3)
之前的文章依赖是1对1或1多对的,但每个任务的前置任务都只有1个. 本文的核心在于一个任务依赖于多个任务的执行完成,如上图所示,这个任务就是fact,只有new和dviduser两个任务都完成的情况下 ...
- webpack 编译ES6
虽然js的es6是大势之趋,但很多浏览器还没有完全支持ES6语法,webpack可以进行对es6打包编译 需要安装的包有 npm init // 初始化 npm install babel-loade ...
- ES 21 - Elasticsearch的高级检索语法 (包括term、prefix、wildcard、fuzzy、boost等)
目录 1 term query - 索引词检索 1.1 term query - 不分词检索 1.2 terms query - in检索 2 prefix query - 前缀检索 3 wildca ...
- Spring Boot:使用Memcached缓存
综合概述 Memcached是一个自由开源的,高性能,分布式内存对象缓存系统.Memcached基于内存的key-value存储,用来存储小块的任意数据,这些数据可以是数据库调用.API调用或者是页面 ...
- HBase 学习之路(七)——HBase过滤器详解
一.HBase过滤器简介 Hbase提供了种类丰富的过滤器(filter)来提高数据处理的效率,用户可以通过内置或自定义的过滤器来对数据进行过滤,所有的过滤器都在服务端生效,即谓词下推(predica ...
- python算法与数据结构-栈(43)
一.栈的介绍 栈作为一种数据结构,是一种只能在一端进行插入和删除操作.它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读 ...
- Shell学习笔记1》转载自runnoob
无论是shell 还是bat,都是与操作系统结合非常紧密的东西,所以在此占坑,希望有朝一日能够把这些东西融会贯通,于是在此占坑~ 学习地址:http://www.runoob.com/linux/li ...