日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

浅谈yield

發布時間:2023/12/10 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 浅谈yield 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

c#1.0使用foreach 語句可以輕松地迭代集合。在c#1.0中,創建枚舉器仍需要做大量的工作。c#2.0添加了yield語句,以便于創建枚舉器。下面我們淺談下yield的使用:

1、包含yield語句的方法或屬性稱為迭代塊迭代塊必須聲明為返回IEnumerator或IEnumerable接口。這個塊可以包含多個yield return語句或yield break語句,但不能包含return語句

??????yield return語句返回集合的一個元素,并移動到下一個元素上。

??????yield break語句:停止迭代

yield 語句只能出現在 iterator 塊中,該塊可用作方法、運算符或訪問器的體。這類方法、運算符或訪問器的體受以下約束的控制:

不允許不安全塊。

方法、運算符或訪問器的參數不能是 ref 或 out。

yield 語句不能出現在匿名方法中。有關更多信息,請參見匿名方法(C# 編程指南)。

當和 expression 一起使用時,yield return 語句不能出現在 catch 塊中或含有一個或多個 catch 子句的 try 塊中。有關更多信息,請參見異常處理語句(C# 參考)。

?2、yield語句從本質上講是運用了延遲計算(Lazy evaluation或delayed evaluation)的思想。在Wiki上可以找到延遲計算的解釋:將計算延遲,直到需要這個計算的結果的時候才計算,這樣就可以因為避免一些不必要的計算而改進性能,在合成一些表達式時候還可以避免一些不必要的條件,因為這個時候其他計算都已經完成了,所有的條件都已經明確了,有的根本不可達的條件可以不用管了。

??????延遲計算來源自函數式編程,在函數式編程里,將函數作為參數來傳遞,你想呀,如果這個函數一傳遞就被計算了,那還搞什么搞,如果你使用了延遲計算,表達式在沒有使用的時候是不會被計算的,比如有這樣一個應用:x=expression,將這個表達式賦給x變量,但是如果x沒有在別的地方使用的話這個表達式是不會被計算的,在這之前x里裝的是這個表達式。舉個例子,linq就是運用了延遲計算的思想。看下面的代碼:

var?result?=?from?book?in?books
????
where
?book.Title.StartWiths(“t”)
????select?book
if(state?>?0
)
{
????
foreach(var?item?in
?result)
????{
????????
//….
???
}
}

result是一個實現了IEnumerable接口的類(Linq里,所有實現了IEnumerable接口的類都被稱作sequence),對它的foreach或者while的訪問必須通過它對應的IEnumeratorMoveNext()方法,如果我們把一些耗時的或者需要延遲的操作放在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被處理了,也許這也算是一種延遲計算吧。

?

講了yield的一些基礎,覺得有必要講下IEnumeratorIEnumerable接口區別:

?

public interface IEnumerable
{
??? IEnumerator GetEnumerator();
}
?
public interface IEnumerator
{
??? bool MoveNext();
??? void Reset();
?
??? Object Current { get; }
}


?1、一個Collection要支持foreach方式的遍歷,必須實現IEnumerable接口(亦即,必須以某種方式返回IEnumerator object)。
?
2
IEnumerator object具體實現了iterator(通過MoveNext()Reset()Current)。
?
3
、從這兩個接口的用詞選擇上,也可以看出其不同:IEnumerable是一個聲明式的接口,聲明實現該接口的class可枚舉(enumerable的,但并沒有說明如何實現枚舉器(iterator);IEnumerator是一個實現式的接口IEnumerator object就是一個iterator
?
4
IEnumerableIEnumerator通過IEnumerableGetEnumerator()方法建立了連接,client可以通過IEnumerableGetEnumerator()得到IEnumerator object,在這個意義上,將GetEnumerator()看作IEnumerator objectfactory method也未嘗不可。


IEnumerator?是所有枚舉數的基接口。??
???
?
枚舉數只允許讀取集合中的數據。枚舉數無法用于修改基礎集合。?這也是為什么說“不要在foreach循環中修改元素的原因
“.
???
?
最初,枚舉數被定位于集合中第一個元素的前面。Reset?也將枚舉數返回到此位置。在此位置,調用?? Current?會引發異常。因此,在讀取?Current?的值之前,必須調用?MoveNext?將枚舉數提前到集合的第一個元素。
??
???
?
在調用?? MoveNext?? ?? Reset?? 之前,Current?? 返回同一對象。MoveNext?? ?? Current?? 設置為下一個元素。??

???
?
在傳遞到集合的末尾之后,枚舉數放在集合中最后一個元素后面,且調用?? MoveNext?? 會返回?? false。如果最后一次調用?? MoveNext?? 返回?? false,則調用?? Current?? 會引發異常。若要再次將?? Current?? 設置為集合的第一個元素,可以調用?? Reset,然后再調用?? MoveNext??
???
?
只要集合保持不變,枚舉數就將保持有效。如果對集合進行了更改(例如添加、修改或刪除元素),則該枚舉數將失效且不可恢復,并且下一次對?? MoveNext?? ?? Reset?? 的調用將引發?? InvalidOperationException如果在?? MoveNext?? ?? Current?? 之間修改集合,那么即使枚舉數已經無效,Current?? 也將返回它所設置成的元素。
??
???
?
枚舉數沒有對集合的獨占訪問權;因此,枚舉一個集合在本質上不是一個線程安全的過程。甚至在對集合進行同步處理時,其他線程仍可以修改該集合,這會導致枚舉數引發異常。若要在枚舉過程中保證線程安全,可以在整個枚舉過程中鎖定集合,或者捕捉由于其他線程進行的更改而引發的異常。??

?

?

代碼下載:/Files/qlb5626267/YieldDemo.rar

參考文獻:你必須知道的.net

轉載于:https://www.cnblogs.com/qlb5626267/archive/2009/05/08/1452517.html

總結

以上是生活随笔為你收集整理的浅谈yield的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。