VS 2012 C# 5.0 规范:迭代器

本文内容

使用迭代器块实现的函数成员称为迭代器 (iterator)。

只要相应函数成员的返回类型是枚举器接口 enumerator 或可枚举接口 enumerable 之一,迭代器块就可用作该函数成员的函数体。它可以作为 method-body、operator-body 或 accessor-body 出现,而不能将事件、实例构造函数、静态构造函数和析构函数作为迭代器来实现。

当使用迭代器块实现函数成员时,为该函数成员的形参列表指定任何 ref 或 out 形参将产生编译时错误。

本文只是 C#5.0 规范中的内容,主要是下载 Demo 看看,这玩意用法挺多。

下载 Demo

枚举器 enumerator 接口



枚举器接口 (enumerator interface) 为非泛型接口 System.Collections.IEnumerator 和泛型接口 System.Collections.Generic.IEnumerator<T> 的所有实例化。

简洁起见,将这些接口分别表示为 IEnumerator 和 IEnumerator<T>。

可枚举 enumerable 接口



可枚举接口 (enumerable interface) 为非泛型接口 System.Collections.IEnumerable 和泛型接口 System.Collections.Generic.IEnumerable<T> 的所有实例化。

简洁起见,将这些接口分别表示为 IEnumerable 和 IEnumerable<T>。

产生类型 yield type



迭代器产生一系列值,所有值的类型均相同。此类型称为迭代器的产生类型 (yield type)。

  • 返回 IEnumerator 或 IEnumerable 的迭代器的产生类型是 object。

  • 返回 IEnumerator<T> 或 IEnumerable<T> 的迭代器的产生类型是 T。

枚举器 enumerator 对象



如果返回枚举器接口类型的函数成员是使用迭代器块实现的,调用该函数成员不会立即执行迭代器块中的代码。而是先创建并返回一个枚举器对象 (enumerator object)。此对象封装了在迭代器块中指定的代码,并且在调用该枚举器对象的 MoveNext 方法时执行该迭代器块中的代码。枚举器对象具有下列特点:

  • 它实现了 IEnumerator 和 IEnumerator<T>,其中 T 为迭代器的产生类型。

  • 它实现了 System.IDisposable。
  • 它以传递给该函数成员的实参值(如果存在)和实例值的副本进行初始化。
  • 它有四种可能的状态:运行前 (before)、运行中 (running)、挂起 (suspended) 和运行后 (after),并且初始状态为运行前 (before) 状态。

枚举器对象通常是编译器生成的枚举器类的一个实例,它封装了迭代器块中的代码,并实现了枚举器接口,但也可能实现其他方法。如果枚举器类由编译器生成,则该类将直接或间接嵌套在包含该函数成员的类中,它将具有私有可访问性,并且它将具有一个供编译器使用的保留名称。

枚举器对象可实现除上面指定的那些接口以外的其他接口。

下面的各节将描述由枚举器对象所提供的 IEnumerable 和 IEnumerable<T> 接口实现的 MoveNext、Current 和 Dispose 成员的确切行为。

请注意,枚举器对象不支持 IEnumerator.Reset 方法。调用此方法将导致引发 System.NotSupportedException。

MoveNext方法

枚举器对象的 MoveNext 方法封装了迭代器块的代码。调用 MoveNext 方法将执行迭代器块中的代码,并相应设置枚举器对象的 Current 属性。MoveNext 执行的具体操作取决于调用 MoveNext 时的枚举器对象的状态:

  • 如果枚举器对象的状态为运行前 (before),则调用 MoveNext 会:
    • 将状态更改为运行中 (running)。
    • 将迭代器块的形参(包括 this)初始化为实参值以及初始化该枚举器对象时所保存的实例值。
    • 从头开始执行迭代器块,直到执行被中断(如后文所述)。
  • 如果枚举器对象的状态为运行中 (running),则调用 MoveNext 的结果不确定。
  • 如果枚举器对象的状态为挂起 (suspended),则调用 MoveNext 将:
    • 将状态更改为运行中 (running)。
    • 将所有局部变量和形参(包括 this)的值恢复为迭代器块的执行上次挂起时保存的值。注意,这些变量所引用对象的内容可能自上次调用 MoveNext 之后已经发生更改。
    • 恢复执行紧跟在引起执行挂起的 yield return 语句后面的迭代器块,并一直继续,直到执行中断(如后文所述)。
  • 如果枚举器对象的状态为运行后 (after),则调用 MoveNext 将返回 false。

当 MoveNext 执行迭代器块时,可以采用四种方式来中断执行:通过 yield return 语句、通过 yield break 语句、到达迭代器块的末尾以及引发异常并将异常传播到迭代器块之外。

  • 当遇到 yield return 语句时:
    • 计算该语句中给出的表达式,隐式转换为产生类型,并赋给枚举器对象的 Current 属性。
    • 迭代器体的执行被挂起。所有局部变量和形参(包括 this)的值被保存,此 yield return 语句的位置也被保存。如果 yield return 语句在一个或多个 try 块内,则此时与之关联的 finally 块将会执行。
    • 枚举器对象的状态更改为挂起 (suspended)。
    • MoveNext 方法向其调用方返回 true,指示迭代成功前进至下一个值。
  • 当遇到 yield break 语句时:
    • 如果 yield break 语句在一个或多个 try 块内,则与之关联的 finally 块将执行。
    • 枚举器对象的状态更改为运行后 (after)。
    • MoveNext 方法向其调用方返回 false,指示迭代完成。
  • 当遇到迭代器体的结束处时:
    • 枚举器对象的状态更改为运行后 (after)。
    • MoveNext 方法向其调用方返回 false,指示迭代完成。
  • 当引发异常并传播到迭代器块之外时:
    • 通过异常传播机制执行迭代器体内的相应 finally 块。
    • 枚举器对象的状态更改为运行后 (after)。
    • 异常继续传播至 MoveNext 方法的调用方。

Current属性

枚举器对象的 Current 属性将受迭代器块中的 yield return 语句影响。

当枚举器对象处于挂起 (suspended) 状态时,Current 的值为上一次调用 MoveNext 时设置的值。当枚举器对象处于运行前 (before)、运行中 (running) 或运行后 (after) 状态时,访问 Current 的结果不确定。

对于产生类型不是 object 的迭代器,通过枚举器对象的 IEnumerable 实现来访问 Current 的结果对应于通过枚举器对象的 IEnumerator<T> 实现来访问 Current 并将该结果强制转换为 object。

Dispose方法

Dispose 方法用于通过使枚举器对象变为运行后 (after) 状态来清除迭代。

  • 如果枚举器对象的状态为运行前 (before),则调用 Dispose 将把状态更改为运行后 (after)。
  • 如果枚举器对象的状态为运行中 (running),则调用 Dispose 的结果不确定。
  • 如果枚举器对象的状态为挂起 (suspended),则调用 Dispose 将:
    • 将状态更改为运行中 (running)。
    • 执行所有 finally 块,就像最后执行的 yield return 语句是 yield break 语句一样。如果这导致引发异常,并且异常传播到迭代器体之外,则枚举器对象的状态设置为运行后 (after),并且将异常传播到 Dispose 方法的调用方。
    • 将状态更改为运行后 (after)。
  • 如果枚举器对象的状态为运行后 (after),则调用 Dispose 没有任何作用。

可枚举对象 enumerable



如果返回可枚举接口类型的函数成员是使用迭代器块实现的,调用该函数成员不会立即执行迭代器块中的代码。而是先创建并返回一个可枚举对象 (enumerable object)。可枚举对象的 GetEnumerator 方法返回一个封装有迭代器块中指定的代码的枚举器对象,当调用该枚举器对象的 MoveNext 方法时,将执行迭代器块中的代码。可枚举对象具有下列特点:

  • 它实现了 IEnumerable 和 IEnumerable<T>,其中 T 为迭代器的产生类型。
  • 它以传递给该函数成员的实参值(如果存在)和实例值的副本进行初始化。

可枚举对象通常是编译器生成的可枚举类的实例,它封装了迭代器块中的代码,并实现了可枚举接口,但也可能实现其他方法。如果可枚举类由编译器生成,则该类将直接或间接嵌套在包含该函数成员的类中,它将具有私有可访问性,并且它将具有供编译器使用的保留名称。

可枚举对象可实现除上面指定的那些接口以外的其他接口。具体而言,可枚举对象还可实现 IEnumerator 和 IEnumerator<T>,从而使其既可作为可枚举对象,也可作为枚举器对象。在该类型的实现中,首次调用可枚举对象的 GetEnumerator 方法时,将返回可枚举对象本身。对可枚举对象的 GetEnumerator 的后续调用(如果存在),将返回可枚举对象的副本。因此,每个返回的枚举器都有自己的状态,一个枚举器中的更改不会影响其他枚举器。

GetEnumerator方法

可枚举对象实现了 IEnumerable 和 IEnumerable<T> 接口的 GetEnumerator 方法。这两种 GetEnumerator 方法的实现是相同的,都是获取并返回一个可用的枚举器对象。枚举器对象是以初始化该可枚举对象时保存的实例值和实参值进行初始化的,此外,枚举器对象函数如前所述。

示例


下面的 Stack<T> 类使用一个迭代器实现其 GetEnumerator 方法。

规范中的该实例编译有错误,采用 MSDN 中的实例:

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

    private T[] items;

    private 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] = default(T);

        return result;

    }

 

    public IEnumerator<T> GetEnumerator()

    {

        for (int i = count - 1; i >= 0; --i)

            yield return items[i];

    }

 

    IEnumerator IEnumerable.GetEnumerator()

    {

        return GetEnumerator();

    }

 

    public IEnumerable<T> TopToBottom

    {

        get { return this; }

    }

 

    public IEnumerable<T> BottomToTop

    {

        get

        {

            for (int index = 0; index <= count - 1; index++)

            {

                yield return items[index];

            }

        }

    }

 

    public IEnumerable<T> TopN(int itemsFromTop)

    {

        // Return less than itemsFromTop if necessary. 

        int startIndex = itemsFromTop >= count ? 0 : count - itemsFromTop;

 

        for (int index = count - 1; index >= startIndex; index--)

        {

            yield return items[index];

        }

    }

}

若用如下代码:

Stack<int> stack = new Stack<int>();
stack.Push(1);

stack.Push(2);

stack.Push(3);

stack.Push(4);

stack.Push(5);

foreach (int s in stack)

{

    Console.Write(s + " ");

}

stack.Pop();

Console.Write("\n");

foreach (int s in stack)

{

    Console.Write(s + " ");

}

Console.Write("\n");

foreach (int s in stack.TopToBottom)

{

    Console.Write(s + " ");

}

Console.Write("\n");

foreach (int s in stack.BottomToTop)

{

    Console.Write(s + " ");

}

Console.Write("\n");

foreach (int s in stack.TopN(2))

{

    Console.Write(s + " ");

}

 

Console.ReadKey();

运行结果:

5 4 3 2 1
4 3 2 1

4 3 2 1

1 2 3 4

4 3

参考资料


下载 Demo

时间: 2024-08-05 02:33:03

VS 2012 C# 5.0 规范:迭代器的相关文章

OpenGL ES SL 3.0规范中以前的attribute改成了in varying改成了out

       OpenGL ES和OpenGL的图标 关于"OpenGL ES SL 3.0规范中以前的attribute改成了in varying改成了out"这个问题,做一阐述: 1.关键字的小修改大概由如下两点决定 第一,先考虑一个成本原则 一个关键字的定义是否修改,是由熟练程序员在使用该关键字时的思维成本来决定的. 当然,还有一个原则,是由初学者的学习成本来决定的,这时一条市场原则(微软喜欢这个原则). attribute改成in,varying 改成out,恰巧符合上面两条原

json-rpc 2.0规范解读

JSON-RPC2.0规范由JSON-RPC工作组([email protected])维护,发布于2010-03-26(基于2009-05-24的版本), 最近的更新于2013-01-04. 整体来说,2.0版本的JSON-RPC规范改动的很小,大的改动大概有3点: 参数可以用数组或命名参数 批量请求的细节明确化了 错误处理的机制标准化了 与1.0版本的兼容性 建议2.0规范的实现兼容1.0协议,但是不强制要求,如果不能兼容,建议给出友好提示. 请求和响应报文加了个参数表示协议的版本号:jso

JSON-RPC 2.0规范 翻译 中文版

JSON-RPC 2.0规范 起源日期: 2010-03-26(基于2009-05-24的版本) 修正: 2013-01-04 作者: JSON-RPC 工作组 <[email protected]> 1 概述 JSON-RPC是一个无状态的.轻量级的远程过程调用(RPC)协议.本规范主要围绕它的处理方式定义了几个数据结构和规则.这个概念可用于在同一进程中.套接字或HTTP之间.或其他很多消息传递的环境中传输数据.它使用JSON (RFC 4627)作为数据格式. JSON-RPC的设计很简单

BPMN 2.0规范

.1. BPMN 2.0是什么呢? 业务流程模型注解(Business Process Modeling Notation - BPMN)是 业务流程模型的一种标准图形注解.这个标准 是由对象管理组(Object Management Group - OMG)维护的. 基本上,BPMN规范定义了任务看起来怎样的,哪些结构可以 与其他进行连接,等等.这就意味着 意思不会被误解. 标准的早期版本(1.2版以及之前)仅仅限制在模型上, 目标是在所有的利益相关者之间形成通用的理解, 在文档,讨论和实现业

json-rpc 1.0规范解读

JSON可能是这个地球上最简单的文本数据格式了,可读.灵活.数据量小,编解码方便.速度快,对Unicode和特殊字符支持的好.对比下XML,就知道额外的各种标签节点需要浪费多少字节数.JSON字符默认都要使用Unicode形式,所有非ACSII字符都可以用\uXXXX表示,而不需要额外的转义.相比之下,XML里需要使用转义或是CDATA(类似HTML里的PRE标签).或是Base64才能表示特殊数据.当然缺点也很明显,比二进制数据结构的数据量大,编解码慢,没有完备的类型系统,表达能力有限. JS

IronCAD Design Collaboration Suite 2012 HF1 14.0 1DVD

Mentor.Graphics.O-in 3.0 Linux 1DVD Avid Media Composer 6.0.0 MacOSX 1DVD IBM.Rational.Requisitepro.7.0-ISO 1CD Schlumberger (ex Waterloo Hydrogeologic) Visual HELP v2.2.0.2 1CD TerraBuilder 6.0.1 1CD TerraExplorer Pro 6.0.1 1CD Terragate 6.0.1 1CD A

VAST3.0规范

VAST3.0视频广告投放规范 Posted on 2014年2月15日 1.术语 随着视频广告行业的发展,某些术语已经得到了广泛的采用.以下定义该文档中与视频广告投放相关的一些术语: 广告荚(Ad Pod):广告荚是一序列线性广告(Linear ads )来回播放,像多个电视插播广告,属于一个商业的暂停休息. 伴随广告(Companion Ad):通常是页面的显示横幅广告(banner)或富媒体(rich media)广告,这些广告出现在视频播放器之外.伴随广告在相关In-Stream广告结束

(译) JSON-RPC 2.0 规范(中文版)

1.概述 JSON-RPC是一个无状态且轻量级的远程过程调用(RPC)协议. 本规范主要定义了一些数据结构及其相关的处理规则.它允许运行在基于socket,http等诸多不同消息传输环境的同一进程中.其使用JSON(RFC 4627)作为数据格式. 它为简单而生! 2.约定 文档中关键字"MUST"."MUST NOT"."REQUIRED"."SHALL"."SHALL NOT"."SHOULD

[转载] SON-RPC 2.0 规范(中文版)

原文: http://wiki.geekdream.com/Specification/json-rpc_2.0.html restful api泛滥, 正好配合jsonrpc规范. 起源时间: 2010-03-26(基于2009-05-24版本) 更新: 2013-01-04 作者: JSON-RPC工作组< [email protected] > 原文链接: http://www.jsonrpc.org/specification 翻译: leozvc < [email protec