欢迎光临
我们一直在努力

C# 2.0 Specification(迭代器)(二)-.NET教程,C#语言

建站超值云服务器,限时71元/月

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 全文完)

赞(0)
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com 特别注意:本站所有转载文章言论不代表本站观点! 本站所提供的图片等素材,版权归原作者所有,如需使用,请与原作者联系。未经允许不得转载:IDC资讯中心 » C# 2.0 Specification(迭代器)(二)-.NET教程,C#语言
分享到: 更多 (0)

相关推荐

  • 暂无文章