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

2021-04-25 00:28

阅读:489

标签:eva   技术   针对   case   输出   string   set   reac   ring   

 

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

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

            Listint> list = new Listint>() { 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).ToListint>();
            list[0] = 11;
            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 persons = ctx.GetTable();

            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语句合并了对年龄和姓名条件的查询。

如果Person表中的值如下:

技术分享图片

根据上面查询将返回:

Name:Steve  Age:21

Name:Jessica  Age:22

如果采用主动求值:

            var temp1 = (from p in persons where p.Age > 20 select p).ToList();
            //省略
            var temp2 = from p in temp1 where p.Name.IndexOf(e) > 0 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个建议》陆敏技

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

标签:eva   技术   针对   case   输出   string   set   reac   ring   

原文地址:http://www.cnblogs.com/farmer-y/p/7930173.html


评论


亲,登录后才可以留言!