1. 程式人生 > >【轉】編寫高質量代碼改善C#程序的157個建議——建議28:理解延遲求值和主動求值之間的區別

【轉】編寫高質量代碼改善C#程序的157個建議——建議28:理解延遲求值和主動求值之間的區別

ons ati rgs 理解 問題 效率 sele 而不是 reac

建議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("\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 > 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<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個建議》陸敏技

【轉】編寫高質量代碼改善C#程序的157個建議——建議28:理解延遲求值和主動求值之間的區別