当前位置:文档之家› LINQ从基础到项目实战

LINQ从基础到项目实战

INQ从基础到项目实战
52
L
3.1 LINQ to OBJECT基础
3.1.1 LINQ to OBJECT概述
LINQ to OBJECT是用于操作内存对象的LINQ编程接口,包含了大量的查询操作
符,针对内存中的集合对象进行操作。
代码演示
下面是一段使用标准查询操作符where和select对一个数组里面的元素进行查询
的代码。
string[] greetings = { "hello", "hello LINQ", "how are you " };
var items =
from s in greetings
where s.Length > 10 //where关键字是LINQ to OBJECT编程接口的标准操作符
select s; //select关键字是LINQ to OBJECT编程接口的标准操作符
//输出满足条件的所有字符串
foreach (var item in items)
Console.WriteLine(item);
专家讲解
从代码可以看出使用LINQ to OBJECT的语法与C#语言语法无缝结合,代码简洁易
读。使用LINQ to OBJECT不仅可以对数组中的元素进行查询,还可以对C#语言中的大
部分集合(只要该集合实现了IEnumerable接口或者IEnumerable接口)对象进行
查询。例如,有一个ArrayList集合对象,该集合对象所包含的元素是自定义
Customer类的实例,如果想查询符合某个条件的Customer实例,完全可以通过使用
LINQ to OBJECT来实现。
3.1.2 IEnumerable泛型接口、序列和标准查询操作符
LINQ to OBJECT的实现基于IEnumerable泛型接口、序列(sequences)以及标
准查询操作符(Standard Query Operators)等基本概念。其中,IEnumerable泛型接
口是使用C# 2.0泛型技术实现的一个接口,该接口与IEnumerable类似,允许对接口内
部的元素进行列举操作;序列是一个专门术语,表示一个实现了IEnumerable接口
的集合对象。
代码演示
例如,下面的代码定义了一个内部元素为string类型的序列。
//定义一个序列,使用泛型接口时类型T要明确指定具体类型
IEnumerable strSequence ;
INQ从基础到项目实战
52
L
3.1 LINQ to OBJECT基础
3.1.1 LINQ to OBJECT概述
LINQ to OBJECT是用于操作内存对象的LINQ编程接口,包含了大量的查询操作
符,针对内存中的集合对象进行操作。
代码演示
下面是一段使用标准查询操作符where和select对一个数组里面的元素进行查询
的代码。
string[] greetings = { "hello", "hello LINQ", "how are you " };
var items =
from s in greetings
where s.Length > 10 //where关键字是LINQ to OBJECT编程接口的标准操作符
select s; //select关键字是LINQ to OBJECT编程接口的标准操作符
//输出满足条件的所有字符串
foreach (var item in items)
Console.WriteLine(item);
专家讲解
从代码可以看出使用LINQ to OBJECT的语法与C#语言语法无缝结合,代码简洁易
读。使用LINQ to OBJECT不仅可以对数组中的元素进行查询,还可以对C#语言中的大
部分集合(只要该集合实现了IEnumerable接口或者IEnumerable接口

)对象进行
查询。例如,有一个ArrayList集合对象,该集合对象所包含的元素是自定义
Customer类的实例,如果想查询符合某个条件的Customer实例,完全可以通过使用
LINQ to OBJECT来实现。
3.1.2 IEnumerable泛型接口、序列和标准查询操作符
LINQ to OBJECT的实现基于IEnumerable泛型接口、序列(sequences)以及标
准查询操作符(Standard Query Operators)等基本概念。其中,IEnumerable泛型接
口是使用C# 2.0泛型技术实现的一个接口,该接口与IEnumerable类似,允许对接口内
部的元素进行列举操作;序列是一个专门术语,表示一个实现了IEnumerable接口
的集合对象。
代码演示
例如,下面的代码定义了一个内部元素为string类型的序列。
//定义一个序列,使用泛型接口时类型T要明确指定具体类型
IEnumerable strSequence ;

21 63 4 5
第3章LINQ to OBJECT
53
LINQ to OBJECT的大部分操作是针对序列的。标准查询操作符本质上是一些扩展
方法(Extension Methods),这些扩展方法定义在静态类System.Linq.Enumerable中,
其原型的第一个参数(带this修饰符的参数)是IEnumerable类型。由于这些方法
都是扩展方法,它们可以在IEnumerable实例对象上直接调用,无需为调用传递一
个类型为IEnumerable的对象作为第一个参数。也有一些标准查询操作符不是扩展
方法,只是普通的静态方法,需要使用标准的静态方法语法进行调用,这些非扩展方法
一般只用来实现一些更复杂的功能。由于历史的原因,在C#语言中存在一些早期的集
合类型,这些类型实现了IEnumerable接口,但是并不支持IEnumerable接口。这意
味着不能够直接在这些类的实例对象上使用标准查询操作符,但是可以通过使用Cast
或者OfType标准查询操作符将这些不支持IEnumerable接口的集合类型转化成支持
IEnumerable接口的集合类型。使用这种方法,标准查询操作符可以支持C#语言中
的所有集合类型。
要在代码中使用LINQ to OBJECT标准查询操作符,需要在代码中添加using
System.Linq指令,以引入必要的命名空间。标准查询操作符对象具体实现在
System.Core.dll文件中,这个文件的引用会被集成开发环境(如Visual Studio 2008)在
每次新建工程时自动加入工程引用中。
代码演示
下面的【实例3-1】使用标准查询操作符Where进行查询操作。
//使用标准查询操作符
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq; //必须的命名空间
namespace Sample
{
public class Test
{
static void Main(string[] args)
{
string[] strArray = {"one", "two", "three", "four", "five", "six",
"seven", "eight", "nine", "ten" };
//使用标准查询操作符Where,当然也

可以使用查询表达式语法书写
IEnumerable items = strArray.Where(p => p.StartsWith("t"));
//遍历输出满足条件的元素
foreach (string item in items)
Console.WriteLine(item);
//使输出窗口暂停以利于观察运行结果
Console.Read();
}
}
}
21 63 4 5
第3章LINQ to OBJECT
53
LINQ to OBJECT的大部分操作是针对序列的。标准查询操作符本质上是一些扩展
方法(Extension Methods),这些扩展方法定义在静态类System.Linq.Enumerable中,
其原型的第一个参数(带this修饰符的参数)是IEnumerable类型。由于这些方法
都是扩展方法,它们可以在IEnumerable实例对象上直接调用,无需为调用传递一
个类型为IEnumerable的对象作为第一个参数。也有一些标准查询操作符不是扩展
方法,只是普通的静态方法,需要使用标准的静态方法语法进行调用,这些非扩展方法
一般只用来实现一些更复杂的功能。由于历史的原因,在C#语言中存在一些早期的集
合类型,这些类型实现了IEnumerable接口,但是并不支持IEnumerable接口。这意
味着不能够直接在这些类的实例对象上使用标准查询操作符,但是可以通过使用Cast
或者OfType标准查询操作符将这些不支持IEnumerable接口的集合类型转化成支持
IEnumerable接口的集合类型。使用这种方法,标准查询操作符可以支持C#语言中
的所有集合类型。
要在代码中使用LINQ to OBJECT标准查询操作符,需要在代码中添加using
System.Linq指令,以引入必要的命名空间。标准查询操作符对象具体实现在
System.Core.dll文件中,这个文件的引用会被集成开发环境(如Visual Studio 2008)在
每次新建工程时自动加入工程引用中。
代码演示
下面的【实例3-1】使用标准查询操作符Where进行查询操作。
//使用标准查询操作符
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq; //必须的命名空间
namespace Sample
{
public class Test
{
static void Main(string[] args)
{
string[] strArray = {"one", "two", "three", "four", "five", "six",
"seven", "eight", "nine", "ten" };
//使用标准查询操作符Where,当然也可以使用查询表达式语法书写
IEnumerable items = strArray.Where(p => p.StartsWith("t"));
//遍历输出满足条件的元素
foreach (string item in items)
Console.WriteLine(item);
//使输出窗口暂停以利于观察运行结果
Console.Read();
}
}
}

INQ从基础到项目实战
54
L
专家讲解
该实例是一个C#控制台应用程序,实现的逻辑是使用Where标准查询操作符在数
组strArray中查询以“t”开头的字符串并输出。
结果验证
运行结果如图3-1所示。
本例代码使用了标准查询操作符Where对数
组里面的元素进行查询,正如代码所

示,Where
操作符返回一个IEnumerable对象,也就是一
个序列对象。在LINQ to OBJECT中,大部分的
标准查询操作符都返回一个序列对象。但需要注
意的是,返回的序列对象内部包含的元素并不是
在标准查询操作符被调用的时刻马上创建的,而是在当列举此序列对象元素的代码被执
行时,系统动态利用yield关键字来创建的。本例中,下面代码的执行只会产生一个空
的序列对象。
IEnumerable items = strArray.Where(p => p.StartsWith("t"));
此序列对象内部元素的创建是在下面代码执行时,系统利用yield关键字动态
完成的。
//列举序列对象代码
foreach (string item in items)
Console.WriteLine(item);
LINQ的这个特性称为延时查询(Deferred Query)。延时查询会给程序带来意想不
到的查询。例如,可以定义一个查询,多次列举查询结果,当被查询的数据在多次列举
之间发生变化时,多次查询的结果是不同的,每次列举都将最新的数据查询出来。
代码演示
下面的【实例3-2】说明了这一点。
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
namespace Sample
{
public class Test
{
static void Main(string[] args)
{
图3-1程序的运行结果
INQ从基础到项目实战
54
L
专家讲解
该实例是一个C#控制台应用程序,实现的逻辑是使用Where标准查询操作符在数
组strArray中查询以“t”开头的字符串并输出。
结果验证
运行结果如图3-1所示。
本例代码使用了标准查询操作符Where对数
组里面的元素进行查询,正如代码所示,Where
操作符返回一个IEnumerable对象,也就是一
个序列对象。在LINQ to OBJECT中,大部分的
标准查询操作符都返回一个序列对象。但需要注
意的是,返回的序列对象内部包含的元素并不是
在标准查询操作符被调用的时刻马上创建的,而是在当列举此序列对象元素的代码被执
行时,系统动态利用yield关键字来创建的。本例中,下面代码的执行只会产生一个空
的序列对象。
IEnumerable items = strArray.Where(p => p.StartsWith("t"));
此序列对象内部元素的创建是在下面代码执行时,系统利用yield关键字动态
完成的。
//列举序列对象代码
foreach (string item in items)
Console.WriteLine(item);
LINQ的这个特性称为延时查询(Deferred Query)。延时查询会给程序带来意想不
到的查询。例如,可以定义一个查询,多次列举查询结果,当被查询的数据在多次列举
之间发生变化时,多次查询的结果是不同的,每次列举都将最新的数据查询出来。
代码演示
下面的【实例3-2】说明了这一点。
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
namespac

e Sample
{
public class Test
{
static void Main(string[] args)
{
图3-1程序的运行结果

21 63 4 5
第3章LINQ to OBJECT
55
int[] intArray = new int[] { 9, 8, 7 };
//下面的查询表达式等价于代码IEnumerable ints = intArray.Select(i => i);
var ints = from i in intArray
select i;
//第一次列举
foreach (int i in ints)
Console.WriteLine(i);
//修改元素的值
intArray[0] = 999;
intArray[1] = 888;
intArray[2] = 777;
Console.WriteLine("---------");
//第二次列举
foreach (int i in ints)
Console.WriteLine(i);
Console.Read();
}
}
}
专家讲解
该实例是一个C#控制台应用程序,程序首先定义了一个数组对象intArray,然后
使用select操作符从数组中查询元素生成一个序列ints,然后分两次列举序列ints,
在两次列举中间修改数组中的元素值,两次列举的结果不同。该程序说明了LINQ的延
时查询特性。
结果验证
运行结果如图3-2所示。
本例中,当调用Select标准查询操
作符时,一个空的IEnumerable对象
生成,当使用foreach开始列举此序列内
部的元素时,相应的元素被一个一个地
生成,并填充到这个空的
IEnumerable对象中去。在完成第一
次列举后,修改原始数据,在执行第二
次列举时,对数据的修改马上被体现出来。如果不想使用延时查询,即要求查询马
上执行,可以在查询表达式上调用一些转换方法,如ToArray、ToList以及
ToDictionary等方法,来缓存结果数据。
图3-2程序的运行结果
21 63 4 5
第3章LINQ to OBJECT
55
int[] intArray = new int[] { 9, 8, 7 };
//下面的查询表达式等价于代码IEnumerable ints = intArray.Select(i => i);
var ints = from i in intArray
select i;
//第一次列举
foreach (int i in ints)
Console.WriteLine(i);
//修改元素的值
intArray[0] = 999;
intArray[1] = 888;
intArray[2] = 777;
Console.WriteLine("---------");
//第二次列举
foreach (int i in ints)
Console.WriteLine(i);
Console.Read();
}
}
}
专家讲解
该实例是一个C#控制台应用程序,程序首先定义了一个数组对象intArray,然后
使用select操作符从数组中查询元素生成一个序列ints,然后分两次列举序列ints,
在两次列举中间修改数组中的元素值,两次列举的结果不同。该程序说明了LINQ的延
时查询特性。
结果验证
运行结果如图3-2所示。
本例中,当调用Select标准查询操
作符时,一个空的IEnumerable对象
生成,当使用foreach开始列举此序列内
部的元素时,相应的元素被一个一个地
生成,并填充到这个空的
IEnumerable对象中去。在完成第一
次列举后,修改原始数据,在执行第二
次列举时,对数据的修改马上被体现出来。如果不想使用延时查询,即要求查询马
上执行,可以在查询表达

式上调用一些转换方法,如ToArray、ToList以及
ToDictionary等方法,来缓存结果数据。
图3-2程序的运行结果

INQ从基础到项目实战
56
L
代码演示
上例的代码修改成如下的【实例3-3】。
//不使用延时查询
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
namespace Sample
{
public class Test
{
static void Main(string[] args)
{
int[] intArray = new int[] { 9, 8, 7 };
//使用ToArray方法进行转换
IEnumerable ints = intArray.Select(i => i).ToArray();
foreach (int i in ints) //第一次列举
Console.WriteLine(i);
intArray[0] = 999; //修改元素的值
intArray[1] = 888;
intArray[2] = 777;
Console.WriteLine("---------");
foreach (int i in ints) //第二次列举
Console.WriteLine(i);
}
}
}
结果验证
运行结果如图3-3所示。
本例中,由于在查询表达式时调用了
ToArray方法,该查询将立即执行,并将查询
结果保存在整数类型数组ints中,两次列举数
组ints中的元素,输出结果相同。
许多标准查询操作符的方法原型接收一个
类型为Func的委托参数,该委托类型定义在
System.Linq空间中。图3-3程序的运行结果
INQ从基础到项目实战
56
L
代码演示
上例的代码修改成如下的【实例3-3】。
//不使用延时查询
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
namespace Sample
{
public class Test
{
static void Main(string[] args)
{
int[] intArray = new int[] { 9, 8, 7 };
//使用ToArray方法进行转换
IEnumerable ints = intArray.Select(i => i).ToArray();
foreach (int i in ints) //第一次列举
Console.WriteLine(i);
intArray[0] = 999; //修改元素的值
intArray[1] = 888;
intArray[2] = 777;
Console.WriteLine("---------");
foreach (int i in ints) //第二次列举
Console.WriteLine(i);
}
}
}
结果验证
运行结果如图3-3所示。
本例中,由于在查询表达式时调用了
ToArray方法,该查询将立即执行,并将查询
结果保存在整数类型数组ints中,两次列举数
组ints中的元素,输出结果相同。
许多标准查询操作符的方法原型接收一个
类型为Func的委托参数,该委托类型定义在
System.Linq空间中。图3-3程序的运行结果

21 63 4 5
第3章LINQ to OBJECT
57
代码演示
定义在System.Linq空间中的委托代码如下。
//定义在System.Linq空间中的委托Func
public delegate T Func< T >();
public delegate T Func< A0, T >( A0 arg0 );
public delegate T Func ( A0 arg0, A1 arg1 );
public delegate T Func( A0 arg0, A1 arg1, A2 arg2 );
public delegate T Func ( A0 arg0, A1 arg1, A2 arg2, A3 arg3 );
专家讲解
本书第2章在讲解表达式树相关内容时,介绍过一个Lambda表达式本质上是一个
匿名方法,该匿名方法可

以赋值给上述Func类型的委托变量。标准查询操作符的方法
原型接收一个类型为Func的委托参数,表明了这些标准查询操作符可以接收一个
Lambda表达式作为参数。下面的代码是标准查询操作符Where的原型定义。
//标准查询操作符Where扩展方法原型
public static IEnumerable Where(
this IEnumerable source, //此扩展方法应用于IEnumerable实例对象
Func predicate); //接收一个泛型委托参数,参数类型为Func
对比System.Linq空间中的委托Func定义代码如下。
public delegate T Func< A0, T >( A0 arg0 );
//这里Func< A0, T >( A0 arg0 )中的A0与Where扩展方法原型中Func中的T对应
//这里Func< A0, T >( A0 arg0 )中的T与Where扩展方法原型中Func中的bool对应
可以看出,操作符Where能够接收一个匿名方法(或者Lambda表达式),该匿名
方法返回值应该为bool类型,还必须接收一个参数arg0。下面的代码使用了Where操
作符。
string[] strArray = {"one", "two", "three", "four", "five", "six",
"seven", "eight", "nine", "ten" };
//使用标准查询操作符Where
IEnumerable items = strArray.Where(p => p.StartsWith("t"));
foreach (string item in items)
Console.WriteLine(item);
代码中,Where操作符接收了一个Lambda表达式参数p=>p.StartsWith("t"),与
此Lambda表达式相等价的方法定义代码如下。
bool MyMethod(string p)
{
return p.StartsWith("t"); //StartsWith方法的返回值为bool类型
}
LINQ中,Lambda表达式被大量地作为查询操作符方法参数使用。
21 63 4 5
第3章LINQ to OBJECT
57
代码演示
定义在System.Linq空间中的委托代码如下。
//定义在System.Linq空间中的委托Func
public delegate T Func< T >();
public delegate T Func< A0, T >( A0 arg0 );
public delegate T Func ( A0 arg0, A1 arg1 );
public delegate T Func( A0 arg0, A1 arg1, A2 arg2 );
public delegate T Func ( A0 arg0, A1 arg1, A2 arg2, A3 arg3 );
专家讲解
本书第2章在讲解表达式树相关内容时,介绍过一个Lambda表达式本质上是一个
匿名方法,该匿名方法可以赋值给上述Func类型的委托变量。标准查询操作符的方法
原型接收一个类型为Func的委托参数,表明了这些标准查询操作符可以接收一个
Lambda表达式作为参数。下面的代码是标准查询操作符Where的原型定义。
//标准查询操作符Where扩展方法原型
public static IEnumerable Where(
this IEnumerable source, //此扩展方法应用于IEnumerable实例对象
Func predicate); //接收一个泛型委托参数,参数类型为Func
对比System.Linq空间中的委托Func定义代码如下。
public delegate T Func< A0, T >( A0 arg0 );
//这里Func< A0, T >( A0 arg0 )中的A0与Where扩展方法原型中Func中的T对


//这里Func< A0, T >( A0 arg0 )中的T与Where扩展方法原型中Func中的bool对应
可以看出,操作符Where能够接收一个匿名方法(或者Lambda表达式),该匿名
方法返回值应该为bool类型,还必须接收一个参数arg0。下面的代码使用了Where操
作符。
string[] strArray = {"one", "two", "three", "four", "five", "six",
"seven", "eight", "nine", "ten" };
//使用标准查询操作符Where
IEnumerable items = strArray.Where(p => p.StartsWith("t"));
foreach (string item in items)
Console.WriteLine(item);
代码中,Where操作符接收了一个Lambda表达式参数p=>p.StartsWith("t"),与
此Lambda表达式相等价的方法定义代码如下。
bool MyMethod(string p)
{
return p.StartsWith("t"); //StartsWith方法的返回值为bool类型
}
LINQ中,Lambda表达式被大量地作为查询操作符方法参数使用。


相关主题
文本预览
相关文档 最新文档