Linq的基本用法
Linq,语言集成查询(language intergrated query)是一组用于C#的扩展。使用Linq可以对集合等内存数据进行查询,还可以查询数据库(datebase)、XML(标准通用标记语言)数据,分别称为Linq to object,Linq to datebase和Linq to XML。这里谈的是Linq to object。
Linq的写法类似于数据库SQL语句的查询语法。
from 变量名 in 集合 where 条件 select 结果变量
其中,变量名是一个临时变量,集合是数组或其他集合对象,条件一般是一个针对变量的逻辑表达式,而结果变量一般是针对变量名的表达式(可以是变量自己)。例如:
表示从数组
arr
中找到大于5的元素(
n
),并且最终选出这些
n
。
如果是选出这些数的平方,则是:
针对满足条件的数,还可以先进行降序排序,然后再输出结果,如:
示例1
查询
int
数组中大于的数字,并按照大小顺序排列:
static void Main(string[] args) {
Random rnd = new Random();
// 示例1,查询int数组中大于10的数字,并按照大小顺序排列:
int[] arr = new int[20];
for (int i = 0; i < arr.Length; i++) arr[i] = rnd.Next(20);
var m = from n in arr where n > 10 orderby n descending select n;
foreach (var n in m)
Console.Write(n + " ");
Console.WriteLine();
}
运行结果:

从上面的例子可以看到,
Linq
查询语法跟SQL查询语法很相似,但它要将
from
放到前面,这样变量的类型才好推断,方便集成开发环境进行智能感知(intellisence)。
Linq
查询的结果返回的是一个枚举器对象,实现了
IEnumerable
接口,这个对象的类型一般很复杂,用
var
类型来表示,让编译器自动推断其类型。这个
var
对象可以用
foreach
来进行遍历。有的
Linq
查询返回的结果是“可查询对象”,实现
IQuerable
接口,由于
IQuerable
接口是
IEnumerable
接口的子接口,可以对它进行更复杂的操作。
值得注意的是:这个枚举器
var
对象并没有开始执行真正的查询,只有当用
foreach
来进行遍历时,查询过程才真正地执行。
示例2
一个稍微复杂的Linq查询,使用group分组功能:
static void Main(string[] args) {
// 示例2,一个稍微复杂的Linq查询,使用group分组功能:
string[] languages = { "Java", "C#", "C++", "Delphi", "VB.net", "VC.net", "Perl", "Python" };
var query = from item in languages
group item by item.Length into lengthGroups
orderby lengthGroups.Key
select lengthGroups;
foreach (var group in query) {
Console.WriteLine("strings of length{0}", group.Key);
foreach (var str in group) {
Console.WriteLine(str);
}
}
}
运行结果:
在这个例子中,按照字符长短(
length
)对数据进行分组(
group
)并放入
lengthGroups
变量中,按分组的关键字(
.Key
)进行排序。所得到的枚举器用双重的
foreach
循环进行遍历。
Linq的查询方法
Linq定义了大约40个查询操作符,如
select
、
from
、
in
、
where
以及
orderby
等。
1.Linq的两种写法
一种形式是查询表达式语法(query expression syntax),另一种更接近SQL语法的查询方式,可读性更好。
另一种形式是查询方法语法(method syntax),主要利用
System.Linq.Enumerable
类中定义的扩展方法和Lambda表达式方法进行查询。例如:
List<int> arr = new List<int>() { 1, 2, 3, 4, 5, 6, 7 };
var result = arr.Where(a => a > 3).Sum();
Console.WriteLine(result);
这段代码中,用到了两个扩展方法。
一个是
Where
扩展方法,需要传入一个
Func<int, bool>
类型的泛型委托,它表示对一个元素(
int
类型的变量)进行判断(
a > 3
),返回值是
bool
类型。这里是直接把
a => a > 3
这个Lambda表达式传递给了
Where
方法。
另一个是
Sum
扩展方法,它计算了
Where
扩展方法返回的集合的和。
使用
where
等子句,实际上是调用了
Enumerable
类中定义的扩展方法。
2.扩展方法
这里所谓的“扩展方法(extension methods)”是在
static
的类中定义的全局函数,它可以带
this
参数,表示对某种对象上施加以一个方法,编译器会自动地将它转成对扩展方法的调用。
示例
对String的扩展方法:
using System;
public static class ExtensionMethodString
{
public static int WordCount(this string s) {
string[] words = s.Split(" ,;.!".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
return words.Length;
}
}
class Demo
{
static void Main(string[] args){
string s = "Hello world,C#!";
int cnt = s.WordCount();
Console.WriteLine(cnt);
}
}
output:
3
在这里,对
string
对象进行了扩展,添加了
WordCount
方法,但是不是在
string
类的内部添加的,而是单独定义了一个
static
类
ExtensionMethodString
,其中定义了
static
方法
WordCount
,其第一个参数(
string
类型)前面加了个特殊的this,从而表明它是一个特殊的对
string
类型的扩展方法。编译器会将
s.WordCount()
自动翻译成
ExtensionMethodString.WordCount
调用。
扩展方法是一个很有用的机制,它在不改变原类的定义的情况下,给原来“添加”了一个方法,实际上是一种语法糖。
扩展方法是从C#3.0 开始引入的,它的作用之一就是为Linq服务的。在Linq技术上,.NET的设计者在类库中定义了一系列的扩展方法来方便用户操作集合对象,这些扩展方法构成了Linq的查询操作符。
3.Linq的查询运算符
Linq的各种查询运算符实际上是定义在
System.Linq.Enumerable
等类中的一系列扩展方法,这些扩展方法是对
IEnumerable
等对象的扩展。所以可以用
var result = arr.Where(a => a > 3).Sum()
这样的调用方法来进行调用。
Linq的标准查询运算符有多达40多个,如下表。这些方法中有一小部分可以有等价的查询表达式关键字,如
where
关键字等价于
Where
方法,类似的还有
select
,
group
,
orderby
,
join
等。
从执行时间上来看,各个标准查询运算符可以分为两类,一类是立即执行,一类是延迟执行。返回单一值的方法(如
Average
和
Sum
)会立即执行。而返回序列的方法会延迟查询执行,因为它返回一个可枚举(
IEnumerable
)的对象,只有当用
foreach
进行遍历或者再次施以
Sum
等操作时才会真正执行。也可以说,查询运算符分两类,一类是“中间点”(如
Where
),它可以继续施加其他查询运算符;一类是“结束的”(如
Sum
),它的结果不能再施加其他查询运算符。
下面是常用运算符及其简单介绍:
Where:过滤出满足条件的。
Select:取出元素(可以映射到新的对象)。
First:取出序列第一个元素。
Take:取出部分元素。
Single:取出序列的唯一一个元素,如果元素个数不是1个,则报错。
FirstOrDefault:取得序列第一个元素,如果没有一个元素,则返回默认值。
Distinct:取出序列中非重复元素。
Orderby:排序。
Reverse:反序。
Concat:连接两个序列;相当于SQL的Union all。
Contains:序列是否包含指定元素。
Except:获得两个序列的差集。
Intersect:获得两个序列的交集。
Average:计算平均值。
Min:最小元素。
Max:最大元素。
Sum:元素总和。
Count:元素数量。