22迭代器 22.1迭代器塊 迭代器塊就是產(chǎn)生值的有序序列的語(yǔ)句塊。迭代器塊通過一個(gè)或多個(gè)yield語(yǔ)句區(qū)別于常規(guī)語(yǔ)句塊。
l yield return 語(yǔ)句產(chǎn)生迭代的下一個(gè)值。
l yield break 語(yǔ)句指明迭代完成。
迭代器塊可以被用作一個(gè)方法體(method-body)、運(yùn)算符體(operator-body)、訪問器體(accessor-body),前提是對(duì)應(yīng)函數(shù)成員的返回類型是枚舉器(enumerator)接口之一或者可枚舉(enumerable)接口之一。
迭代器塊在C#語(yǔ)法中不什么獨(dú)特的元素。它們?cè)趲讉(gè)方面受到限制,并且主要的作用在函數(shù)成員聲明的語(yǔ)義上,但它們?cè)谡Z(yǔ)法上只是語(yǔ)句塊而已。
當(dāng)一個(gè)函數(shù)成員使用迭代器塊實(shí)現(xiàn)時(shí),對(duì)于正式參數(shù)列表指定任何ref或out參數(shù)將導(dǎo)致編譯時(shí)錯(cuò)誤。
return語(yǔ)句出現(xiàn)在迭代器塊中將導(dǎo)致編譯時(shí)錯(cuò)誤(但yield return語(yǔ)句是允許的)。
在迭代器塊中包含不安全上下文(§18.1)將導(dǎo)致編譯時(shí)錯(cuò)誤。即使是當(dāng)?shù)髀暶鲀?nèi)嵌在不安全上下文中,迭代器塊也總是定義為一個(gè)安全上下文。
22.1.1枚舉器接口 枚舉器接口(enumerator interface)是System.Collections.IEnumerator接口以及System.Collections.Generic.IEnumerator<T>的所有實(shí)例。在本章,這些接口將相應(yīng)地作為IEnumerator和IEnumerator<T>而引用。
22.1.2可枚舉接口 可枚舉接口(enumerable interface)是System.Collections.IEnumerable接口和System.Collections.Generic.IEnumerable<T>的所有實(shí)例。在本章,這些接口將相應(yīng)地作為IEnumerable和IEnumerable<T>而被引用。
22.1.3Yield 類型 迭代器塊生成具有相同類型的所有值的序列。給類型被稱為迭代器塊的yield類型(yield type)。
l 迭代器塊的yield類型通常用于實(shí)現(xiàn)返回IEnumerator或IEnumerable是對(duì)象的函數(shù)成員。
l 迭代器塊的yield類型通常用于實(shí)現(xiàn)返回IEnumerator<T>或IEnumerable<T>是T的函數(shù)成員。
22.1.4 this 訪問 在類的實(shí)例成員的迭代器塊內(nèi),this表達(dá)式被歸類為值。該值的類型就是類類型,在這個(gè)類型可以采用這種用法,這個(gè)值就是成員被調(diào)用時(shí)的對(duì)象的引用。
在結(jié)構(gòu)的實(shí)例成員的迭代器塊內(nèi),this表達(dá)時(shí)被歸類作為一個(gè)變量。該變量的類型就是結(jié)構(gòu)類型,在這個(gè)結(jié)構(gòu)中它可以采用這種用法。該變量表示一個(gè)成員被調(diào)用時(shí)的對(duì)應(yīng)結(jié)構(gòu)的一個(gè)拷貝。在結(jié)構(gòu)實(shí)例成員的迭代器塊內(nèi),this變量的行為就好像是結(jié)構(gòu)類型的一個(gè)值參數(shù)。
22.2枚舉對(duì)象 當(dāng)返回枚舉器接口類型的函數(shù)成員使用迭代器塊實(shí)現(xiàn)時(shí),調(diào)用函數(shù)成員并不會(huì)立即執(zhí)行迭代器塊中的代碼。相反,枚舉器對(duì)象(enumerator object)將被創(chuàng)建和返回。該對(duì)象封裝了在迭代器塊中指定的代碼,當(dāng)枚舉器對(duì)象的MoveNext方法被調(diào)用時(shí),迭代器塊中的代碼就會(huì)執(zhí)行。枚舉器對(duì)象有如下的特征。
l 它實(shí)現(xiàn)了IEnumerator和IEnumerator<T>,T是迭代器塊的yield類型(產(chǎn)生類型)。
l 它實(shí)現(xiàn)了System.IDisposable。
l 它被使用實(shí)參值(如果有的話)的拷貝而初始化,而實(shí)例值將被傳遞給函數(shù)成員。
l 它有四個(gè)潛在的狀態(tài)before、running、suspended和after,并且它在before狀態(tài)之前被初始化。
枚舉器對(duì)象通常是一個(gè)編譯器生成的枚舉器類實(shí)例,它封裝了迭代器語(yǔ)句塊中的代碼,并且實(shí)現(xiàn)了枚舉器接口,但其它實(shí)現(xiàn)方法也是可以的。如果一個(gè)枚舉器類是由編譯器生成的,這個(gè)類將會(huì)是內(nèi)嵌的,在包含函數(shù)成員的類中,類將具有私有可訪問性,并且該類具有一個(gè)保留為編譯器所用的名字(§2.4.2)。
枚舉器對(duì)象可以實(shí)現(xiàn)比在此指定的更多接口。
隨后幾節(jié)描述了由IEnumerable和IEnumerable<T>接口實(shí)現(xiàn)的MoveNext、Current、和Dispose成員的確切行為,這兩個(gè)接口由枚舉對(duì)象提供。
請(qǐng)注意,枚舉器對(duì)象并不支持IEnumerator.Reset方法。調(diào)用該方法將會(huì)拋出System.NotSupportedException異常。
22.2.1MoveNext方法 枚舉器對(duì)象的MoveNext方法封裝了迭代器塊的代碼。調(diào)用MoveNext方法將執(zhí)行迭代器內(nèi)的代碼,并將枚舉對(duì)象的Current屬性設(shè)置為合適的值。由MoveNext方法執(zhí)行的精確動(dòng)作,取決于當(dāng)MoveNext方法被調(diào)用時(shí)枚舉器對(duì)象的狀態(tài)。
l 如果枚舉器對(duì)象狀態(tài)是before,調(diào)用MoveNext
n 將把狀態(tài)改為running。
n 將把迭代器塊的參數(shù)(包括this)初始化為,當(dāng)枚舉器對(duì)象被初始化而保存的實(shí)參值和實(shí)例值。
n 從開始執(zhí)行迭代器塊直到執(zhí)行被中斷(如下面所描述的)。
l 如果枚舉器對(duì)象的狀態(tài)是running,調(diào)用MoveNext的結(jié)果是未指定的。
l 如果枚舉器對(duì)象的狀態(tài)是suspended,調(diào)用MoveNext
n 將把狀態(tài)改為running。
l 恢復(fù)所有局部變量和參數(shù)(包括this)的值為迭代器最后一次掛起(suspended)時(shí)執(zhí)行狀態(tài)的值。請(qǐng)注意,由這些變量所引用的任何對(duì)象的內(nèi)容,都可能因?yàn)榍耙淮螌?duì)MoveNext的調(diào)用而改變。
n 在引發(fā)執(zhí)行掛起的yield return 語(yǔ)句之后重新開始執(zhí)行迭代器塊,并且這個(gè)狀態(tài)會(huì)繼續(xù)直到執(zhí)行被中斷(如下所描述)。
l 如果枚舉器對(duì)象的狀態(tài)是after,那么調(diào)用MoveNext將返回false。
當(dāng)MoveNext執(zhí)行迭代器塊時(shí),有四種方法可以中斷執(zhí)行:通過一個(gè)yield return 語(yǔ)句,通過一個(gè)yield break語(yǔ)句,到達(dá)迭代器塊的結(jié)束點(diǎn),以及一個(gè)異常被拋出,并被傳播到迭代器塊之外。
l 當(dāng)遇到一個(gè)yield return 語(yǔ)句時(shí)(§22.4),將會(huì)發(fā)生如下情況
n 在該語(yǔ)句中被給定的表達(dá)式將被計(jì)算,隱式地轉(zhuǎn)換到產(chǎn)生類型(yield type),并被賦值給枚舉對(duì)象的Current屬性。
n 迭代器體的執(zhí)行將被掛起。所有局部變量的值和參數(shù)(包括this)被保存,該yield return 語(yǔ)句的位置也被保存。如果yield return 語(yǔ)句在一個(gè)或多個(gè)try塊之內(nèi),與之關(guān)聯(lián)的finally塊在此時(shí)將不會(huì)執(zhí)行。
n 枚舉器對(duì)象的狀態(tài)被改為suspended。
n MoveNext方法對(duì)調(diào)用方返回true,表明迭代器成功前進(jìn)到下一個(gè)值。
l 當(dāng)遇到y(tǒng)ield break 語(yǔ)句時(shí),將會(huì)發(fā)生如下情況
n 如果yield break 語(yǔ)句在一個(gè)或多個(gè)try塊之內(nèi),與之關(guān)聯(lián)的finally語(yǔ)句將被執(zhí)行。
n 枚舉器對(duì)象的狀態(tài)被改為after。
n MoveNext方法對(duì)調(diào)用方返回false,表明迭代已經(jīng)完成。
l 當(dāng)遇到迭代器塊的結(jié)束點(diǎn)時(shí),將會(huì)發(fā)生如下情況。
n 枚舉器對(duì)象的狀態(tài)被改為after。
n MoveNext方法對(duì)調(diào)用方返回false,表明迭代已經(jīng)完成。
l 當(dāng)一個(gè)異常被拋出并被傳播到迭代器塊之外時(shí),將會(huì)發(fā)生如下情況。
n 在迭代器塊之內(nèi)將會(huì)由于異常傳播(exception propagation)而執(zhí)行合適的finally塊。
n 枚舉器對(duì)象的狀態(tài)被改為after。
n 對(duì)于MoveNext方法的調(diào)用方來(lái)說(shuō),異常傳播將會(huì)繼續(xù)。
22.2.2 Current屬性 枚舉器對(duì)象的Current屬性受到迭代器塊的yield return 語(yǔ)句的影響。
當(dāng)枚舉器對(duì)象處于suspended狀態(tài)時(shí),Current的值就是最后一次調(diào)用MoveNext時(shí)被設(shè)置的值。當(dāng)枚舉器對(duì)象處于before、running或after狀態(tài)時(shí),訪問Current的所得結(jié)果是未指定的。
對(duì)于一個(gè)具有非object類型的yield 類型迭代器塊,通過枚舉器對(duì)象的IEnumerable實(shí)現(xiàn)訪問Current所得實(shí)現(xiàn),對(duì)應(yīng)于通過枚舉器對(duì)象的IEnumerator<T>訪問Current所得實(shí)現(xiàn),并將結(jié)果轉(zhuǎn)換到object類型。
22.2.3 Dispose方法 Dispose方法通過將枚舉器對(duì)象的狀態(tài)置為after,以清理迭代結(jié)果。
l 如果枚舉器對(duì)象的狀態(tài)是before,調(diào)用Dispose將改變其狀態(tài)為after。
l 如果枚舉器對(duì)象的狀態(tài)是running,調(diào)用Dispose的結(jié)果是為指定的。
l 如果枚舉器對(duì)象的狀態(tài)是suspended,調(diào)用Dispose將
n 改變其狀態(tài)為running。
n 執(zhí)行finally塊,就好像最后執(zhí)行的yield return語(yǔ)句是一個(gè)yield break語(yǔ)句。如果這里引發(fā)一個(gè)異常被拋出并傳播到迭代器體之外,枚舉器對(duì)象的狀態(tài)將被置為after,并且該異常將被傳播給Dispose方法的調(diào)用方。
n 改變其狀態(tài)為after。
l 如果枚舉器對(duì)象的狀態(tài)為after,調(diào)用Dispose沒有效果。
22.3可枚舉對(duì)象 當(dāng)返回一個(gè)可枚舉接口類型的函數(shù)成員使用迭代器塊實(shí)現(xiàn)時(shí),調(diào)用函數(shù)成員不會(huì)立即執(zhí)行迭代器塊代碼。相反,一個(gè)可枚舉對(duì)象(enumerable object)將被創(chuàng)建并返回。可枚舉對(duì)象的GetEnumerator方法返回一個(gè)枚舉器對(duì)象,它封裝了在迭代器塊中指定的代碼,當(dāng)枚舉器對(duì)象的MoveNext方法被調(diào)用時(shí),將觸發(fā)迭代器塊代碼的執(zhí)行。可枚舉對(duì)象具有如下特征。
l 它實(shí)現(xiàn)了IEnumerable和IEnumerable<T>接口,這里T是迭代器塊的產(chǎn)生類型(yield type)。
l 它使用實(shí)參值的拷貝進(jìn)行初始化(如果有的話),并將實(shí)例值傳遞給函數(shù)成員。
可枚對(duì)象通常是一個(gè)由編譯器生成的可枚舉類的實(shí)例,該類封裝了迭代器塊的代碼,并實(shí)現(xiàn)了可枚舉接口,但其他實(shí)現(xiàn)方法也是可以的。如果可枚舉類由編譯器生成,該類將內(nèi)嵌在包含函數(shù)成員的類中,并具有私有可訪問性,以及一個(gè)為編譯器所保留使用的名字(§2.4.2)。
可枚對(duì)象可以實(shí)現(xiàn)比在此說(shuō)明的更多接口。特別的是,可枚舉對(duì)象也可以實(shí)現(xiàn)IEnumerator和IEnumerator<T>接口,這使得它既可以作為一個(gè)可枚舉對(duì)象又可作為枚舉器對(duì)象。在那個(gè)實(shí)現(xiàn)類型中,對(duì)可枚舉對(duì)象的GetEnumerator方法的首次調(diào)用,將返回可枚舉對(duì)象自身。對(duì)于該可枚舉對(duì)象的GetEnumerator方法的后續(xù)調(diào)用,如果有的話,將會(huì)返回可枚舉對(duì)象的一個(gè)拷貝。因此,每次返回的枚舉器將有它自己的狀態(tài),并且在一個(gè)枚舉器中所作的改變不會(huì)影響另一個(gè)枚舉器。
22.3.1 GetEnumerator方法 可枚舉對(duì)象提供了IEnumerable和IEnumerable<T>接口的GetEnumerator方法的一個(gè)實(shí)現(xiàn)。這兩個(gè)GetEnumerator方法共享一個(gè)公共實(shí)現(xiàn),它是用來(lái)得到并返回一個(gè)有效的枚舉器對(duì)象。
枚舉器對(duì)象使用實(shí)參值進(jìn)行初始化,當(dāng)可枚舉對(duì)象被初始化時(shí)其實(shí)例值將被保存,另一方面,枚舉器對(duì)象函數(shù)將如§22.2所描述。
|
溫馨提示:喜歡本站的話,請(qǐng)收藏一下本站!