一、 关于LINQ
LINQ 英文全称是“Language-Integrated Query”,中文为“语言集成查询”,它是微软首席架构师、Delphi 之父和C# 之父——Anders Hejlsberg 提出的并由其团队着力打造的一组用于c#和Visual Basic语言的扩展,为 C# 和 Visual Basic 语言语法提供强大的查询功能。微软从2003年开始启动LINQ的开发,在VisualStudio2008中开始加入LINQ功能。
LINQ提供的便利:
1)使用一种简化的方式编写查询语句;
2)通过消除运行时错误和捕捉编译时错误减少开发时间;
3)直接在开发语言中提供对LINQ的IntelliSense和调试支持;
4)消除关系数据和面向对象开发之间的障碍;
5)提供与数据源无关的统一查询语法。
LINQ的一大特性是可以对多种数据源查询,而且查询的语法相同,这为开发人员处理不同的数据源的数据提供了极大的便利。
LINQ 能查询的数据有但不限于:
1)对象,LINQ to Objects。比如集合、数组、字符串等等。
2)关系数据,分为 LINQ to DataSet 和 LINQ to SQL,前者用于 DataSet 查询,后者用于 SQL Server 数据库查询。
3)XML,LINQ to XML。
二、 操作符和LINQ
下面代码演示从一个字符串数组中获取所有以“t”结尾的字符串,并打印。
这是一个简单的LINQ表达式,其中where和select是LINQ众多标准查询操作符中的两个,select是投影操作符,它在某个系列(即实现了Ienumerable
可能有部分读者看了Enumerable和Queryable类提供了的静态方法,如Enumerable类中提供了where的方法为:
在观照from fruit in fruits where fruit.EndsWith("t") select fruit这样的查询表达式会有些疑惑,因为我们通常使用静态方法都是这样用的:方法名.静态方法(方法参数),但是在上面查询表达式中,这些静态方法却被当做关键字的方式来使用。其实LINQ提供了查询语法和方法语法这两种语法来编写查询功能,像上面是查询语法,实现var fs = from fruit in fruits where fruit.EndsWith("t") select fruit这样的查询功能,我们还可以用LINQ提供的方法语法来实现:
var fs = fruits.where(f=>f.EndsWith(“t”));
可见在方法语法使用标准查询操作符和Enumerable类提供的静态方法是吻合的。写过T-SQL语句的读者对于LINQ的查询语法都很容易理解和阅读,这是人容易理解的方式,但其实CLR并不理解查询语法,CLR能够理解方法语法,因此编译一个LINQ的查询表达式时,查询语法将会被转换为方法语法。
三、 LINQ操作符类型类型
可根据标准查询操作符的操作“类型”进行分类,如把它们分成投影、限制、排序、联接、分组、串联、聚合、集合、生成、转换、元素、相等、量词、分割等。
类型 | 操作符名称 |
投影操作符 | Select,SelectMany |
限制操作符 | Where |
排序操作符 | OrderBy,OrderByDescending,ThenBy,ThenByDescending,Reverse |
联接操作符 | Join,GroupJoin |
分组操作符 | GroupBy |
串联操作符 | Concat |
聚合操作符 | Aggregate,Average,Count,LongCount,Max,Min,Sum |
集合操作符 | Distinct,Union,Intersect,Except |
生成操作符 | Empty,Range,Repeat |
转换操作符 | AsEnumerable,Cast,OfType,ToArray,ToDictionary,ToList,ToLookup |
元素操作符 | DefaultIfEmpty,ElementAt,ElementAtOrDefault,First,Last,FirstOrDefault, LastOrDefault,Single,SingleOrDefault |
相等操作符 | SequenceEqual |
量词操作符 | All,Any,Contains |
分割操作符 | Skip,SkipWhile,Take,TakeWhile |
为便于下面代码的演示,这里先定义一些公共代码。我们定义一个学生类Student,一个学校类School,然后定义一个Student的集合类Sudents和一个学校的集合schools。
1、投影操作符
1) Select 将序列中的每个元素投影到新表中。
代码演示从students数据源中查询年龄为9岁的学生,使用Select操作符从返回结果序列中返回Name和Class两列。
输出结果如下:
Li Lei
Han Meimei
2) SelectMany 将序列的每个元素投影到 IEnumerable
输出结果如下:
Li
Lei
Han
Meimei
Li
Ming
Zou
Qi
Wang
Long
2、限制操作符
1)Where 对序列中值进行过滤,它不启动查询的执行,也就是查询是延迟的,只有当枚举对象时where才应用到数据上
下面示例从学生集合中过滤名叫LiLei的学生,代码如下:
输出结果如下:
Li Lei
上面代码建立查询语句,但这个语句并没用立即执行,而是在后面用foreach遍历stu对象时,查询语句才是执行,这就是所谓的延迟执行。LINQ的查询语句执行有两种方式,一种是延迟执行,一种是立即执行。任何返回单个值的LINQ查询都会立即执行,如我们查询学生集合中年龄7岁的学生个数:var count = (from student in students where student.Age == 7 select student).Count();
3、排序操作符
1)OrderBy 该操作符对序列元素按照选定的键进行升序排序
如对学生集合中的学生按照年龄进行升序排序:
输出结果如下:
Li Lei 9
Han Meimei 9
Zou Qi 7
Wang Long 7
Li Ming 6
2)OrderByDescending 这个操作符和OrderBy相反,它是对序列元素按选定的键进行降序排列
如对学生集合中的学生按照年龄进行降序序排序:
输出结果如下:
Li Lei 9
Han Meimei 9
Zou Qi 7
Wang Long 7
Li Ming 6
3)ThenBy 该操作符实现序列元素按照次关键字进行升序排序
如对学生集合中先按年龄排序再按姓名排序:
输出结果如下:
Li Ming 6
Wang Long 7
Zou Qi 7
Han Meimei 9
Li Lei 9
我们发现ThenBy操作符并不能在查询方法中使用只能在方法语法中使用。熟悉写SQL语句的读者可能会问:IEnumerable
4)ThenByDecending 该操作符和ThenBy相反,它按次关键字降序排序
输出结果如下:
Li Ming 6
Zou Qi 7
Wang Long 7
Li Lei 9
Han Meimei 9
5)Reverse 该操作符对序列中的元素进行反转(逆序)排序
如把学生集合类按逆序输出学生姓名:
输出结果如下:
Wang Long
Zou Qi
Li Ming
Han Meimei
Li Lei
4、联接操作符
1)Join 该操作符类似T-SQL中的inner join ,一个序列通过Join根据匹配条件联接另外一个序列。
如学生集合类根据SchoolId和学校集合类的SchoolId匹配联接,产生一个新的序列。
输出结果如下:
StudentName:Li Lei SchoolName:BeiJing Middle School
StudentName:Han Meimei SchoolName:BeiJing Middle School
StudentName:Li Ming SchoolName:ShangHai Middle School
StudentName:Zou Qi SchoolName:ShangHai Middle School
StudentName:Wang Long SchoolName:BeiJing Middle School
2)GroupJoin 该操作符将主数据源的每一个元素和次数据源的相应的值(根据某个匹配条件)联接起来,返回一个具有层级的结果集
下面代码演示了打印每一所学校名称,同时打印这所学校的学生的名称,代码如下:
输出结果如下:
BeiJing Middle School
Li Lei
Han Meimei
Wang Long
ShangHai Middle School
Li Ming
Zou Qi
GuangZhou Middle School
5、分组操作符
1)GroupBy 该操作符根据一个指定的值将序列中的元素进行分组
下面代码演示了学生集合中根据SchoolId进行分组,代码如下:
输出结果如下:
1
Li Lei
Han Meimei
Wang Long
2
Li Ming
Zou Qi
6、串联操作符
1)Concat 该操作符将两个系列合并成一个系列
下面代码演示将students的Name和schools中SchoolName合并成一个新的系列,代码如下:
输出结果如下:
Li Lei
Han Meimei
Li Ming
Zou Qi
Wang Long
BeiJing Middle School
ShangHai Middle School
GuangZhou Middle School
7、聚合操作符
聚合操作符在一系列的值上执行特定的运算,并返回单个值,如执行求和求平均值等。
1)Aggregate 该操作符将序列中的值进行累积并返回结果
下面演示使用Aggregate操作符返回学生集合中所有的学生名字:
输出结果如下:
Li Lei , Han Meimei , Li Ming , Zou Qi , Wang Long
2)Average 该操作符计算一个数值序列的平均值。注意它只能应用于数值数列
下面代码演示计算学生集合中学生的平均年龄:
输出结果如下:
7.6
3) Count 该操作符计算一个特定集合中元素的个数,返回结果是一个Int32类型的数值
如计算学生集合中学生的个数:
输出结果如下:
5
4)LongCount 该操作符和Count类似也是计算序列中元素的个数,所不同是返回的结果是一个Int64类型的值
输出结果如下:
5
5) Max 该操作符返回一个系列中的最大值。
如计算学生集合中最大的年龄:
输出结果如下:
9
6) Min 该操作符和Max相反,返回的是一个序列中的最小值。
输出结果如下:6
7)Sum 该操作符计算序列中选定值的总和。
计算学生集合中年龄的总和:
输出结果如下:
38
8、集合操作符
集合操作符对元素的集合或序列的集合进行操作,并返回一个集合。
1) Distinct 该操作符删除集合中重复值,返回的结果的元素是互不相同的
如查询学生集合中的年龄有多少种。
输出结果如下:
9
6
7
2)Union 该操作符返回两个系列中每个互不相同的元素,即两个集合的并集∪
我们前面提到Concat操作符,Concat是返回两个集合的所有元素,Union则是返回互不相同的元素。
如查询students的StuId和schools的SchoolId的并集,代码如下:
输出结果如下:
1
2
3
4
5
8
3)Intersect 该操作符返回同时存在两个系列中的元素,也就是返回两个系列元素的交集∩
如查询students的StuId和schools的SchoolId的交集,代码如下:
输出结果如下:
1
2
4) Except 该操作符返回的是系列A有,但系列B没有的元素
如查询students有的编号而schools没有的编号,代码如下:
输出结果如下:
3
4
5
9、生成操作符
生成操作符从现有系列的值中创建新的系列。
1) Empty 该操作符返回一个指定类型的空的 IEnumerable
IEnumerable
下面的代码示例演示了 Empty
输出结果如下:
Solanki, Ajay
Hoeing, Helge
Andersen, Henriette Thaulow
Potra, Cristina
Iallo, Lucio
2) Range 该操作符创建一个数字序列的集合,它包含两个参数,第一个是序列的开始值,第二个参数是产生序列的个数
下面代码演示创建一个从1到5的数字序列,然后每个数字乘以5:
输出结果如下:
5
10
15
20
25
3)Repeat 该操作符创建一个但值序列,将次值重复一定的次数。
下面代码演示创建一个包含三个元素的序列,这些元素都是“test”,代码如下:
输出结果如下:
test
test
test
10、转换操作符
1)AsEnumerable 该操作符将查询的输入以IEnumerable
许多类型都实现了IEnumerable。其中一部分还实现了与IEnumerable一模一样的公共方法。比如说,如果你有一个实现了Where方法的类型MyList(跟IEnumerable
输出结果如下:
调用MyList的Where方法
2
2
2)Case 该操作符将IEnumerable集合中的元素转换为某种指定的类型。这样的好处就是通过提供必要的类型信息,可以将标准查询操作符应用在非泛型集合上
如下面代码演示通过使用Cast操作符,可以将标准操作符应用于查询ArrayList类型对象的数据源(ArrayList没有实现IEnumerable
输出结果如下:
Li Lei
Li Ming
3) OfType 该操作符可以对一个序列进行指定类型的过滤
下面代码演示从ArrayList对象中过滤可以转换为Int类型的元素,代码如下:
输出结果如下:
4
1
2
4)ToArray 该操作符实现从一个IEnumerable序列创建一个数组。该操作符会强制查询立即执行
下面代码演示将students集合中讲学生名字转换为一个字符串数组:
输出结果如下:
5
5)ToDictionary 该操作符将序列分别转换为一对一的Dictionary
输出结果如下:
Key:1,Name:Li Lei
Key:2,Name:Han Meimei
Key:3,Name:Li Ming
Key:4,Name:Zou Qi
Key:5,Name:Wang Long
6) ToList 该操作符将一个IEnumerable序列集合转换为List
输出结果如下:
Li Lei
Han Meimei
Li Ming
Zou Qi
Wang Long
7)LookUp 该操作符将序列转换为一对多的LookUp
输出结果如下:
Grade Three
Li Lei
Han Meimei
Grade One
Li Ming
Zou Qi
Wang Long
11、元素操作符
元素操作符从一个系列返回单个特定的元素
1)DefaultIfEmpty 该操作符将一个空集合替换为包含默认的单个值的集合。在返回序列为空集合时且又需要返回一些对象时,可以通过该操作符返回一个默认值
如果查询students集合中年龄为5岁的学生,因为结果为空,可以指定一个默认值。代码如下所示:
输出结果如下:
默认值
2) ElementAt 该操作符返回集合中给定索引处的元素
如查询students集合中索引为1的元素,代码如下:
输出结果如下:
Han Meimei
3)ElementAtOrDefault 该操作符返回指定索引处的元素
如果该索引超出范围,则返回默认值,如果是引用类型返回为Null,如果是值类型则返回为0。如下面获取索引为100的学生,因为索引范围没有那么大,索引返回为null。
输出结果如下:
返回默认值null
4)First 该操作符返回序列中的第一个元素
如果源系列不包含任何元素,First方法将抛出一个System.InvalidOperationException
的异常。
下面代码演示返回学生集合students的第一个元素。
输出结果如下:
Li Lei
5)Last 与First操作符相反,该操作符返回序列中的最后一个元素
输出结果如下:
Wang Long
6)FirstOrDefault 该 操作符类似First操作符,不同的是,如果没有发现任何元素则返回默认值,元素为引用类型默认值为null,值类型为0
如查询students集合中学生年龄为20的学生的第一个,代码如下:
输出结果如下:
返回默认值null
7)LastOrDefault 该操作符类似Last操作符,不同的是,如果没有发现任何元素返回,则返回默认值,元素为引用类型默认值为null,值类型为0
如查询students集合中学生年龄为20的学生的最后一个,代码如下:
输出结果如下:
返回默认值null
8)Single 该操作符返回系列中满足某一条件的单个元素,如果满足条件不止一个元素或零个,则抛出异常
如获取students中名字为“Li Lei”的学生,代码如下:
输出结果如下:
Li Lei
9)SingleOrDefault 该操作符从一个系列中获取符合某一条件的唯一元素,如果没有符合元素,则返回默认值
元素为引用类型默认值为null,值类型默认值为0.注意如果符合条件的元素有多个,会抛出异常。
如返回students集合中SchoolId为4的学生,代码如下:
输出结果如下:
返回默认值null
12、相等操作符
1)SequenceEqual操作符可以判定两个集合是否相等,返回值为Boolean值
输出结果如下:
True
13.量词操作符
1)All 该操作符判断系列的所有元素是否都满足某一条件,返回值为Boolean值
如判断students集合中是否所有学生名字都以“L”开头,代码如下:
输出结果如下:
False
2)Any 该操作符判断集合是否至少一个元素满足某一条件,返回值为Boolean值
输出结果如下:
True
3)Contains 该操作符判断系列是否包含某个特定的元素,返回值为Boolean
如判断名称集合中是否包含“Li Lei”这个名称,代码如下:
输出结果如下:
True
14、分割操作符
1)Skip 该操作符能够掉到一定的元素到达一个指定位置,并且从该位置开始返回其余的元素
如跳过students集合的前两个元素,读取剩下的元素,代码如下:
输出结果如下:
Li Ming
Zou Qi
Wang Long
2)SkipWhile 该操作符基于特定的谓词略过某些符合条件的元素,返回其余的元素
这里需要特别注意,SkipWhile对数据源进行枚举,从第一个枚举得到的元素开始,如果返回true,则跳过该元素,继续进行枚举操作。但是,如果一旦返回false,则该元素以后的所有元素,都不会再调用SkipWhile。
输出结果为:
9
6
9
4
如果数组numSkipWhile为{9,5,7,9,6,9,4},则输出结果为:
9
5
7
9
6
9
4
3)Take 该操作符返回某个系列中连续的元素子序列,子序列开始于序列的开头,结尾为某个指定的参数,如果指定的参数超过索引,则返回到系列的结尾为止
输出结果为 :
5
7
9
4) TakeWhile 该操作符通过特定的条件选取元素,如果某个元素不符合条件,则从该元素起的元素全部跳过
输出结果为:
5
7