购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

建议28:理解延迟求值和主动求值之间的区别

要理解延迟求值(lazy evaluation)和主动求值(eager evaluation),首先来看一个例子:


List<int>list=new List<int>(){0,1,2,3,4,5,6,7,8,9};

var temp1=from c in list where c>5 select c;

var temp2=(from c in list where c>5 select c).ToList<int>();

list[0]=11;

Console.Write("temp1:");

foreach(var item in temp1)

{

Console.Write(item.ToString()+"");

}

Console.Write("\ntemp2:");

foreach(var item in temp2)

{

Console.Write(item.ToString()+"");

}


上面代码的输出为:


temp1:11 6 7 8 9

temp2:6 7 8 9


在延迟求值的情况下,只是定义了一个查询,而且不是立刻执行。对查询结果的访问每次都会遍历原集合。如上文中对于temp1的迭代,在迭代之前,我们修改了list[0]的值,可以看到,修改直接影响了迭代的输出。对查询调用ToList、ToArray等方法,将会使其立即执行,由于对list[0]的修改是在temp2查询之后进行的,所以针对list[0]的修改不会影响到temp2的结果。

在使用LINQ to SQL时,延迟求值能够带来显著的性能提升。举个例子:如果定义了两个查询,而且采用延迟求值,CLR则会合并两次查询并生成一个最终的查询:


static void Main(string[]args)

{

DataContext ctx=new DataContext("server=192.168.0.102;database=Temp;

uid=sa;pwd=sa123");

Table<Person>persons=ctx.GetTable<Person>();

var temp1=from p in persons where p.Age>20 select p;

//省略

var temp2=from p in temp1 where p.Name.IndexOf('e')>0 select p;

foreach(var item in temp2)

{

Console.WriteLine(string.Format("Name:{0}\tAge:{1}",item.Name,

item.Age));

}

}

[Table(Name="Person")]

class Person

{

[Column]

public string Name{get;set;}

[Column]

public int Age{get;set;}

}


注意 这段代码需要SQL Server数据库的支持。本段代码假设已经存在一个Temp的数据库,其中有一个Person表,表内包含两个字段:


Name,varchar(50)

Age,int


在迭代开始的时候,LINQ to SQL引擎会生成如下的SQL查询语句:


exec sp_executesql N'SELECT[t0].[Name],[t0].[Age]

FROM[Person]AS[t0]

WHERE((

(CASE

WHEN(DATALENGTH(@p0)/2)=0 THEN CONVERT(BigInt,0)

ELSE CONVERT(BigInt,(CONVERT(Int,CHARINDEX(@p0,[t0].[Name])))-1)

END))>@p1)AND([t0].[Age]>@p2)',N'@p0 nchar(1),@p1 int,@p2 int',

@p0=N'e',@p1=0,@p2=20


可以看到,最终的SQL语句中合并了对年龄和姓名的条件查询。如果我们的数据库中存在如下的记录(见表2-3):

表2-3 Person表

那么根据上面的查询条件,数据库将会返回两条记录:


Name:Steve Age:21

Name:Jessica Age:22


如果采用主动求值:


var temp1=(from p in persons where p.Age>20 select p).ToList<Person>();

//省略

var temp2=from p in temp1 where p.Name.IndexOf('e')>0 select p;


会生成下面的语句,可以看到SQL语句仅仅包含对年龄的条件筛选:


exec sp_executesql N'SELECT[t0].[Name],[t0].[Age]

FROM[Person]AS[t0]

WHERE[t0].[Age]>@p0',N'@p0 int',@p0=20


最终,数据库会返回三条语句,如下所示。


Name:Steve Age:21

Name:Jessica Age:22

Name:Lisa Age:23


虽然执行完temp2的查询后返回的结果也是两条记录,但是针对temp2的查询实际是对已经返回到本地的这三条数据进行的筛选。在这个例子中,返回三条或两条语句带来的效率问题似乎并不明显,但是,如果将应用放到一个互联网系统中,在每个地方减少一定的流量,则将会为我们带来客观的性能要求。

事实上,应该仔细体会延迟求值和主动求值之间的区别,体会两者在应用中会带来什么样的输出结果;否则,很有可能会出现一些我们意想不到的Bug。 v3gKYedlCXwTrcsShvfwQCRNFpgAxIcB6wBe40Gu6KFGpyAD+9udj8eN94pc1tfB

点击中间区域
呼出菜单
上一章
目录
下一章
×

打开