程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> C# 2.0 Specification(迭代器)(二)

C# 2.0 Specification(迭代器)(二)

編輯:關於C語言
22.4 yIEld 語句
yIEld語句用於迭代器塊以產生一個枚舉器對象值,或表明迭代的結束。

embedded-statement:(嵌入語句)
...
yield-statement(yIEld語句)

yield-statement:(yIEld 語句)
yIEld return expression ;
yIEld break ;

為了確保和現存程序的兼容性,yield並不是一個保留字,並且 yIEld只有在緊鄰return或break關鍵詞之前才具有特別的意義。而在其他上下文中,它可以被用作標識符。

yIEld語句所能出現的地方有幾個限制,如下所述。

l yIEld語句出現在方法體、運算符體和訪問器體之外時,將導致編譯時錯誤。

l yIEld語句出現在匿名方法之內時,將導致編譯時錯誤。

l yIEld語句出現在try語句的finally語句中時,將導致編譯時錯誤。

l yIEld return 語句出現在包含catch子語句的任何try語句中任何位置時,將導致編譯時錯誤。

如下示例展示了yIEld語句的一些有效和無效用法。

delegate IEnumerable<int> D();

IEnumerator<int> GetEnumerator() {
try {
yIEld return 1; // Ok
yIEld break; // Ok
}
finally {
yield return 2; // 錯誤, yIEld 在finally中
yield break; // 錯誤, yIEld 在 finally中
}





try {
yield return 3; // 錯誤, yIEld return 在try...catch中
yIEld break; // Ok
}
catch {
yield return 4; // 錯誤, yIEld return 在 try...catch中
yIEld break; // Ok
}

D d = delegate {
yield return 5; // 錯誤, yIEld 在匿名方法中
};
}

int MyMethod() {
yIEld return 1; // 錯誤, 迭代器塊的錯誤返回類型
}



從yIEld return 語句中表達式類型到迭代器的產生類型(§22.1.3),必須存在隱式轉換(§6.1)。

yIEld return 語句按如下方式執行。

l 在語句中給出的表達式將被計算(evaluate),隱式地轉換到產生類型,並被賦給枚舉器對象的Current屬性。

l 迭代器塊的執行將被掛起。如果yIEld return 語句在一個或多個try塊中,與之關聯的finally塊此時將不會執行。

l 枚舉器對象的MoveNext方法對調用方返回true,表明枚舉器對象成功前進到下一個項。



對枚舉器對象的MoveNext方法的下一次調用,重新從迭代器塊掛起的地方開始執行。

yeld break 語句按如下方式執行。

l 如果yIEld break 語句被包含在一個或多個帶有finally塊的try塊內,初始控制權將轉移到最裡面的try語句的finally塊。當控制到達finally塊的結束點後,控制將會轉移到下一個最近的try語句的finally塊。這個過程將會一直重復直到所有內部的try語句的finally塊都被執行。

l 控制返回到迭代器塊的調用方。這可能是由於枚舉器對象的MoveNext方法或Dispose方法。



由於yield break語句無條件的轉移控制到別處,所以yIEld break語句的結束點將永遠不能到達。



22.4.1明確賦值
對於以yield return expr 形式的yIEld return 語句stmt



l 像stmt開始一樣,在expr的開頭變量v具有明確的賦值狀態。

l 如果在expr的結束點v被明確賦值,那它在stmt的結束點也將被明確賦值;否則,在stmt結束點將不會被明確賦值。



22.5實現例子
本節以標准C#構件的形式描述了迭代器的可能實現。此處描述的實現基於與Microsoft C#編譯器相同的原則,但這絕不是強制或唯一可能的實現。

如下Stack<T>類使用迭代器實現了GetEnumerator方法。該迭代器依序枚舉了堆棧中從頂到底的元素。

using System;
using System.Collections;
using System.Collections.Generic;

class Stack<T>: IEnumerable<T>
{
T[] items;
int count;

public void Push(T item) {
if (items == null) {
items = new T[4];
}
else if (items.Length == count) {
T[] newItems = new T[count * 2];
Array.Copy(items, 0, newItems, 0, count);
items = newItems;
}
items[count++] = item;
}

public T Pop() {
T result = items[--count];
items[count] = T.default;
return result;
}

public IEnumerator<T> GetEnumerator() {
for (int i = count - 1; i >= 0; --i) yIEld items[i];
}
}





GetEnumerator方法可以被轉換到編譯器生成的枚舉器類的實例,該類封裝了迭代器塊中的代碼,如下所示。

class Stack<T>: IEnumerable<T>
{
...

public IEnumerator<T> GetEnumerator() {
return new __Enumerator1(this);
}

class __Enumerator1: IEnumerator<T>, IEnumerator
{
int __state;
T __current;
Stack<T> __this;
int i;

public __Enumerator1(Stack<T> __this) {
this.__this = __this;
}

public T Current {
get { return __current; }
}

object IEnumerator.Current {
get { return __current; }
}

public bool MoveNext() {
switch (__state) {
case 1: goto __state1;
case 2: goto __state2;
}
i = __this.count - 1;
__loop:
if (i < 0) goto __state2;
__current = __this.items[i];
__state = 1;
return true;
__state1:
--i;
goto __loop;
__state2:
__state = 2;
return false;
}





public void Dispose() {
__state = 2;
}

void IEnumerator.Reset() {
throw new NotSupportedException();
}
}
在先前的轉換中,迭代器塊之內的代碼被轉換成state Machine,並被放置在枚舉器類的MoveNext方法中。此外局部變量i被轉換成枚舉器對象的一個字段,因此在MoveNext的調用過程中可以持續存在。

下面的例子打印一個簡單的從整數1到10的乘法表。該例子中FromTo方法返回一個可枚舉對象,並且使用迭代器實現。

using System;
using System.Collections.Generic;

class Test
{
static IEnumerable<int> FromTo(int from, int to) {
while (from <= to) yIEld return from++;
}

static void Main() {
IEnumerable<int> e = FromTo(1, 10);
foreach (int x in e) {
foreach (int y in e) {
Console.Write("{0,3} ", x * y);
}
Console.WriteLine();
}
}
}

FromTo方法可被轉換成編譯器生成的可枚舉類的實例,該類封裝了迭代器塊中的代碼,如下所示。

using System;
using System.Threading;
using System.Collections;
using System.Collections.Generic;

class Test
{
...

static IEnumerable<int> FromTo(int from, int to) {
return new __Enumerable1(from, to);
}





class __Enumerable1:
IEnumerable<int>, IEnumerable,
IEnumerator<int>, IEnumerator
{
int __state;
int __current;
int __from;
int from;
int to;
int i;

public __Enumerable1(int __from, int to) {
this.__from = __from;
this.to = to;
}

public IEnumerator<int> GetEnumerator() {
__Enumerable1 result = this;
if (Interlocked.CompareExchange(ref __state, 1, 0) != 0) {
result = new __Enumerable1(__from, to);
result.__state = 1;
}
result.from = result.__from;
return result;
}

IEnumerator IEnumerable.GetEnumerator() {
return (IEnumerator)GetEnumerator();
}

public int Current {
get { return __current; }
}

object IEnumerator.Current {
get { return __current; }
}

public bool MoveNext() {
switch (__state) {
case 1:
if (from > to) goto case 2;
__current = from++;
__state = 1;
return true;
case 2:
__state = 2;
return false;
default:
throw new InvalidOperationException();
}
}

public void Dispose() {
__state = 2;
}

void IEnumerator.Reset() {
throw new NotSupportedException();
}
}
}

這個可枚舉類實現了可枚舉接口和枚舉器接口,這使得它成為可枚舉的或枚舉器。當GetEnumerator方法被首次調用時,將返回可枚舉對象自身。後續可枚舉對象的GetEnumerator調用,如果有的話,都返回可枚舉對象的拷貝。因此,每次返回的枚舉器都有其自身的狀態,改變一個枚舉器將不會影響另一個。Interlocked.CompareExchange方法用於確保線程安全操作。



from和to參數被轉換為可枚舉類的字段。由於from在迭代器塊內被修改,所以引入另一個__from字段來保存在每個枚舉其中from的初始值。

如果當__state是0時MoveNext被調用,該方法將拋出InvalidOperationException異常。這將防止沒有首次調用GetEnumerator,而將可枚舉對象作為枚舉器而使用的現象發生。

(C# 2.0 Specification 全文完)
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved