【More Effective C#】延遲求值
2010-10-14 18:30 空逸云 閱讀(949) 評論(5) 收藏 舉報LinQ查詢時,我們不會立即得到返回的數據并生成一個序列.定義該查詢只是為了得到查詢結果所要的執行的步驟,只有開始迭代查詢時,查詢才會真正的執行.這樣,每次執行查詢,都會從頭開始執行查詢的每個步驟,每次遍歷/迭代都會得到全新的結果.這就叫延遲求值.相反.很多時候我們要所見即所得.定義什么查詢都得到唯一的數據列表.這叫主動求值.
理解延遲查詢.舉個不太恰當的例子.如C#的Const.我們定義了一個常量.編譯時其實都是常量值.是不變的.這就像主動求值.而如果是用變量.則每次變量的內容都不同.這是把這個"變量"(得到查詢結果的步驟)插入到代碼之中而已.直到真正迭代調用的時候才執行該步驟,從而得到結果.
private static IEnumerable<TResult> Generate<TResult>(int number, Func<TResult> generator)
{
for (int i = 0; i < number; i++)
yield return generator();
}
private static void LazyEvaluation()
{
Console.WriteLine("Start time for Test One:{0}", DateTime.Now);
var sequence = Generate(10, () => DateTime.Now);
Console.WriteLine("Waiting...\tPress Return");
Console.ReadLine();
Console.WriteLine("Iterating...");
foreach (var value in sequence)
Console.WriteLine(value);
Console.WriteLine("Waiting...\tPress Return");
Console.ReadLine();
Console.WriteLine("Iterating...");
foreach (var value in sequence)
Console.WriteLine(value);
}

上面這段代碼生成了一個序列.迭代該序列2次.可以看到.2次生成的數據不同..這也說明.序列1與序列2共享的是其構造方式,而不是數據.若要主動求值.只需要
var sequence = Generate(10, () => DateTime.Now).ToList();
隨后你可以發現2次生成的數據是一致的.

由于延遲求值的本質,查詢表達式可以操作在無限長度的序列之上.
private IEnumerable<int> AllNumbers()
{
int number = 0;
while (number < int.MaxValue)
yield return number++;
}
public void DisplayMaxNum()
{
var answer = from num in AllNumbers()
select num;
var smallNumbers = answer.Take(10);
foreach (var num in smallNumbers)
Console.WriteLine(num);
}
實驗后可以知道.這段程序的執行速度非常快.因為它實際上只獲取前10個數據.而如果是主動求值.則必須要先得到int.MaxValue的所有值之后再獲取前10個元素.性能可想而知.
如果序列的長度可能為無限的話,那么則必須避免使用需要整個序列的方法,采用延遲查詢能很完美的解決.如果序列并不是無限的.但有需要過濾的條件.則應該把過濾的條件放在最前執行.
var sortedProductsSlow =
from p in products
orderby p.UnitsInStock descending
where p.xx > 10
select p;
var sortedProductsSlow =
from p in products
where p.xx > 10
orderby p.UnitsInStock descending
select p;
第一種實現將比第二種實現有效率,不過在Linq to object的實現中.所有的產品都會被讀取出來再排序.所以兩者并沒有區別.這主要是Linq to object和 Linq to sql的內部實現不同(將會在后面的lambda中介紹).
利用延遲查詢,我們可以創造出更靈活的代碼.更簡潔的實現.所以.除非特別有必要使用主動求值,否則最好使用延遲求值.
出處:http://kongyiyun.cnblogs.com
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。
浙公網安備 33010602011771號