编写高质量代码改善C#程序的157个建议——建议28:理解延迟求值和主动求值之间的区别

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

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

            List<int> list = new List<int>() { , , , , , , , , ,  };
var temp1 = from c in list where c > select c;
var temp2 = (from c in list where c > select c).ToList<int>();
list[] = ;
Console.Write("temp1: ");
foreach (var item in temp1)
{
Console.Write(item + " ");
}
Console.Write("\ntemp2: ");
foreach (var item in temp2)
{
Console.Write(item + " ");
}

输出:

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 > select p;
//省略
var temp2 = from p in temp1 where p.Name.IndexOf('e') > 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语句合并了对年龄和姓名条件的查询。

如果Person表中的值如下:

编写高质量代码改善C#程序的157个建议——建议28:理解延迟求值和主动求值之间的区别

根据上面查询将返回:

Name:Steve  Age:21

Name:Jessica  Age:22

如果采用主动求值:

            var temp1 = (from p in persons where p.Age >  select p).ToList<Person>();
//省略
var temp2 = from p in temp1 where p.Name.IndexOf('e') > select p;

会生成下面的语句:

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

FROM [Person] AS [t0]

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

数据库会返回3条数据

Name:Steve  Age:21

Name:Jessica  Age:22

Name:Lisa  Age:23

虽然temp2的查询返回的结果也是两条记录,但是针对temp2的查询实际是对已经返回到本地的3条数据进行的筛选。这个例子中,返回3条或2条带来的效率问题并不明显,但是,将应用放到互联网系统中,每个地方减少一定的流量,则会给我们带来可观的性能提升。

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

转自:《编写高质量代码改善C#程序的157个建议》陆敏技

上一篇:Linux基础学习一


下一篇:《HBase in Action》 第三章节的学习总结 ---- 如何编写和运行基于HBase的MapReduce程序