• 技术文章 >后端开发 >C#.Net教程

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

    黄舟黄舟2017-01-03 13:04:29原创531

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


    以上就是C# 2.0 Specification(迭代器)(二)的内容,更多相关内容请关注PHP中文网(m.sbmmt.com)!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    专题推荐:C#2.0,Specification
    上一篇:C# 2.0 Specification(迭代器)(一) 下一篇:ASP.NET中日历控件和JS版日历控件的使用方法
    PHP编程就业班

    相关文章推荐

    • 一文聊聊C语言中的字符串操作(大小写转换、比较、排序等)• 分享一道逻辑面试题,看看你能答对吗!• C++设计模式浅识装饰模式• SUNWEN教程之----C#进阶(五)• asp.net 5 开发者的五个阶段

    全部评论我要评论

  • 取消发布评论发送
  • 1/1

    PHP中文网