【轉】編寫高質量代碼改善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("\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:理解延遲求值和主動求值之間的區別