借鑒於該篇博客:http://kb.cnblogs.com/page/42581/
先看兩個方法
public class YieldClasses
{
public static IEnumerable<int> WithNoYied()
{
List<int> list = new List<int>();
for (int i = 0; i < 100; i++)
{
Console.Write(i.ToString());
list.Add(i);
}
return list;
}
public static IEnumerable<int> WithYied()
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine(i.ToString());
yield return i;
}
}
}
分別用執行他們接過是怎樣
YieldClasses.WithNoYied();
YieldClasses.WithYied();??????
令人驚訝的是:第一個函數跟預期的一樣,從1輸到99。而第二個函數卻什么 也沒有輸出!這是神馬回事???
我們再foreach中迭代兩個函數
foreach (var item in YieldClasses.WithNoYied()) { Console.Write(item.ToString()+"-"); }
結果:
withYied的結果

withnoYied的結果:
1233456789101112.。。。。。。。0-1-2-3-4-5-6-7-8-9-10.。。。(突然截圖不行了)
這是神馬回事呢?
從顯示的結果可以看出,在WithNoYied中,是先循環執行完函數中的console.write,再執行main中的write
而在WithYied中,是循環一次,先執行函數中的wirte,再執行main中的write,再循環下一次。。。。
這就是症結所在!明之:yield的延遲執行!
http://kb.cnblogs.com/page/42581/2/ 這里有用IL的方法分析。我就簡單的總結以下吧:
延遲計算(Lazy evaluation或delayed evaluation)在Wiki上可以找到它的解釋:將計算延遲,直到需要這個計算的結果的時候才計算,這樣就可以因為避免一些不必要的計算而改進性能,在合成一些表達式時候還可以避免一些不必要的條件,因為這個時候其他計算都已經完成了,所有的條件都已經明確了,有的根本不可達的條件可以不用管了。反正就是好處很多了。
延遲計算來源自函數式編程,在函數式編程里,將函數作為參數來傳遞,你想呀,如果這個函數一傳遞就被計算了,那還搞什么搞,如果你使用了延遲計算,表達式在沒有使用的時候是不會被計算的,比如有這樣一個應用:x=expression,將這個表達式賦給x變量,但是如果x沒有在別的地方使用的話這個表達式是不會被計算的,在這之前x里裝的是這個表達式。
延遲計算在linq中的應用:
看看下面的方法:
public static void LinqTest() { int[] intArray = {1,3,6,5,9,34 }; int a=0; var result = from item in intArray select item; if(a==0) { foreach(int i in result) { Console.Write(i); } } }
這樣的方法,如果a==0,那會輸出結果(先執行linq操作),但如果a!=0,那么linq操作就不執行!
result是一個實現了IEnumerable接口的類(在Linq里,所有實現了IEnumerable接口的類都被稱作sequence),對它的foreach或者while的訪問必須通過它對應的IEnumerator的MoveNext()方法,如果我們把一些耗時的或者需要延遲的操作放在MoveNext()里面,那么只有等到MoveNext()被訪問,也就是result被使用的時候那些操作才會執行,而給result賦值啊,傳遞啊,什么的,那些耗時的操作都沒有被執行。
如果上面這段代碼,最后由於state小於0,而對result沒有任何需求了,在Linq里返回的結果都是IEnumerable的,如果這里沒有使用延遲計算,那那個Linq表達式不就白運算了么?如果是Linq to Objects還稍微好點,如果是Linq to SQL,而且那個數據庫表又很大,真是得不償失啊,所以微軟想到了這點,這里使用了延遲計算,只有等到程序別的地方使用了result才會計算這里的Linq表達式的值的,這樣Linq的性能也比以前提高了不少,而且Linq to SQL最后還是要生成SQL語句的,對於SQL語句的生成來說,如果將生成延遲,那么一些條件就先確定好了,生成SQL語句的時候就可以更精練了。還有,由於MoveNext()是一步步執行的,循環一次執行一次,所以如果有這種情況:我們遍歷一次判斷一下,不滿足我們的條件了我們就退出,如果有一萬個元素需要遍歷,當遍歷到第二個的時候就不滿足條件了,這個時候我們就可就此退出,后面那么多元素實際上都沒處理呢,那些元素也沒有被加載到內存中來。
延遲計算還有很多惟妙惟肖的特質,也許以后你也可以按照這種方式來編程了呢。寫到這里我突然想到了Command模式,Command模式將方法封裝成類,Command對象在傳遞等時候是不會執行任何東西的,只有調用它內部那個方法他才會執行,這樣我們就可以把命令到處發,還可以壓棧啊等等而不擔心在傳遞過程中Command被處理了,也許這也算是一種延遲計算吧。
本文也只是很淺的談了一下延遲計算的東西,從這里還可以牽扯到並發編程模型和協同程序等更多內容,由於本人才疏學淺,所以只能介紹到這個地步了,上面一些說法也是我個人理解,肯定有很多不妥地方,歡迎大家拍磚。
