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


    建议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 + " ");
                }
                Console.Write("
    temp2: ");
                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 > 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}	Age:{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<Person>();
                //省略
                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个建议》陆敏技

  • 相关阅读:
    创建Android守护进程(底层服务)【转】
    jack server 常见错误解决方法【转】
    kvm初体验——linux之kvm安装及使用qemu工具安装系统【转】
    全志H3-NanoPi开发板SDK之三编译流程【转】
    数字图像处理之二维码图像提取算法(一)【转】
    Ubuntu登陆不进去(已解决)【转】
    可重入函数设计的基本原理【学习笔记】
    Linux文件系统十问---深入理解文件存储方式(rhel6.5,EXT4)【转】
    [RK3288][Android6.0] TS-ADC驱动流程小结【转】
    手把手教你使用eclipse+qemu+gdb来单步调试ARM内核【学习笔记】
  • 原文地址:https://www.cnblogs.com/jesselzj/p/4731495.html
Copyright © 2020-2023  润新知