自C#的第一个版本以来,使用foreach语句可以轻松地迭代集合。在C#1.0中,创建枚举器仍需要做大量的工作。C#2.0添加了yield语句,以便于创建枚举器。yield return语句返回集合的一个元素,并移动到下一个元素上。yield break可停止迭代,如下例所示:
public class HelloCollection
{
public IEnumerator<string> GetEnumerator()
{
yield return "Hello";
yield return "World";
}
}
    包含yield语句的方法或属性也成为迭代块。迭代块必须声明返回IEnumerator或IEnumerable接口,或者这些接口的泛型版本。这个块可以包含多条yield return语句或yield break语句,但不能包含return语句。
    现在可以用foreach语句迭代集合了:
static void Main(string[] args)
{
HelloCollection collection = new HelloCollection();
foreach (string s in collection)
Console.WriteLine(s); }
    使用迭代块,编译器会生成一个yield类型,其中包含一个状态机,如下面的代码段所示。yield类型实现IEnumerator和IDisposable接口的属性和方法。在下面的例子中,可以把yield类型看做内部类Enumerator。外部类的GetEnumerator方法实例化并返回一个新的yield类型。在yield类型中,变量state定义了迭代的当前位置,每次调用MoveNext时,当前位置都会改变。MoveNext封装了迭代块的代码,并设置了current变量的值,从而使Current属性根据位置返回一个对象。
 public class HelloCollection
{
public IEnumerator GetEnumerator()
{
return new Enumerator();
}
public class Enumerator : IEnumerator<string>, IEnumerator, IDisposable
{
private int state;
private string current; public Enumerator(int state) { this.state = state; }
bool System.Collections.IEnumerator.MoveNext()
{
switch (state)
{
case :
current = "Hello";
state = ;
return true;
case :
current = "World";
state = ;
return true;
case :
break;
}
return false;
}
void System.Collections.IEnumerator.Reset()
{
throw new NotSupportedException();
}
string System.Collections.Generic.IEnumerator<string>.Current
{
get { return current; }
}
object System.Collections.IEnumerator.Current
{
get { return current; }
}
void IDisposable.Dispose() { }
}
}
    yield return语句会生成一个枚举器,而不仅仅生成一个包含的项的列表。这个枚举器通过foreach语句调用。从foreach中依次访问每一项时,就会访问枚举器。这样就可以迭代大量的数据,而无需一次把所有的数据都读入内存。
迭代集合的不同方式
    在下面这个比Hello World示例略大但比较真实的示例中,可以使用yield return语句,以不同方式迭代集合的类。类MusicTitles 可以用默认方式通过GetEnumerator方法迭代标题,用Reverse方法逆序迭代标题,用Subset方法迭代子集:
 public class MusicTitles
{
string[] names = { "Tubular Bells", "Hergest Ridge", "Ommadawn", "Platinum" }; public IEnumerator<string> GetEnumerator()
{
for (int i = ; i < ; i++)
yield return names[i];
}
public IEnumerable<string> Reverse()
{
for (int i = ; i >= ; i--)
yield return names[i];
}
public IEnumerable<string> Subset(int index, int length)
{
for (int i = index; i < index + length; i++)
yield return names[i];
}
}
    类支持的默认迭代时定义为返回IEnumerator的GetEnumerator方法。命名的迭代返回IEnumerable。
用yield return返回枚举器
    使用yield return语句还可以完成更复杂的任务,例如,从yield return中返回枚举器。在TicTacToe游戏中有9个域,玩家轮流在这些域中放置一个“十”字或一个圆。这些操作有GameMoves类模拟。方法Cross和Circle是创建迭代器类型的迭代块。变量cross和circle在GameMoves类的构造函数中设置为Cross何Circle方法。这些字段不设置为调用方法,而是设置为用迭代块定义的迭代类型。在Cross迭代块中,将移动操作的信息写到控制台上,并递增移动次数。如果移动次数大于8,就用yield break停止迭代,否则,就在每次迭代中返回yield类型circle枚举变量。Circle迭代块非常类似于Cross迭代块,只是它在每次迭代中返回cross迭代器类型。
 public class GameMoves
{
private IEnumerator cross;
private IEnumerator circle; public GameMoves()
{
cross = Cross();
circle = Circle();
}
private int move = ;
const int MaxMoves = ; public IEnumerator Cross()
{
while (true)
{
Console.WriteLine("Cross, move {0}", move);
if (++move >= MaxMoves)
yield break;
yield return circle;
}
}
public IEnumerator Circle()
{
while (true)
{
Console.WriteLine("Circle, move {0}", move);
if (++move >= MaxMoves) yield break;
yield return cross;
}
}
}
    在客户端程序中,可以使用如下方式使用GameMoves类。将枚举器设置为由game.Cross()返回的枚举器类型,以设置第一次调用。在while循环中,调用enumerator.MoveNext()。第一次调用enumerator.MoveNext()时,会调用Cross方法,Cross方法使用yield语句返回另一个枚举器。返回值可以用Current属性访问,并设置enumerator变量,用于下一次循环:
var game = new GameMoves();
IEnumerator enumerator = game.Cross();
while (enumerator.MoveNext())
enumerator = enumerator.Current as IEnumerator;
程序的输出会显示交替移动的情况,直到最后一次移动:
Cross, move 0
Circle, move 1
Cross, move 2
Circle, move 3
Cross, move 4
Circle, move 5
Cross, move 6
Circle, move 7
Cross, move 8

yield语句的更多相关文章

  1. Python生成器以及yield语句

    生成器是一种暂缓求值的技术,它可以用来生成一系列的值,但不会一次性生成所有的值,而只在需要的时候才计算和生成一个值. 通过yield语句构建生成器 要得到一个生成器,我们需要定义一个函数,这个函数返回 ...

  2. javascript笔记04:let语句 和 yield语句 和 with语句

    1.yield语句: <script type="application/javascript; version=1.7"> function generator() ...

  3. 生成器以及yield语句

    生成器以及yield语句最初的引入是为了让程序员可以更简单的编写用来产生值的序列的代码. 以前,要实现类似随机数生成器的东西,需要实现一个类或者一个模块,在生成数据的同时 保持对每次调用之间状态的跟踪 ...

  4. Python with yield语句

    1.with 语句 语法: with expression as variable 需要引入一个上下文管理协议,实现的方法是为一个类定义 __enter__() 和 __exit__() 方法, 在执 ...

  5. python -迭代器与生成器 以及 iterable(可迭代对象)、yield语句

    我刚开始学习编程没多久,对于很多知识还完全不知道,而有些知道的也是一知半解,我想把学习到的知识记录下来,一是弥补记忆力差的毛病,二也是为了待以后知识能进一步理解透彻时再回来做一个补充. 参考链接: 完 ...

  6. C#:foreach语句,yield语句

    原文:C#:foreach语句,yield语句 1. foreach语句 C#编译器会把foreach语句转换为IEnumerable接口的方法和属性. foreach (Person p in pe ...

  7. Python中生成器和yield语句的用法详解

    Python中生成器和yield语句的用法详解 在开始课程之前,我要求学生们填写一份调查表,这个调查表反映了它们对Python中一些概念的理解情况.一些话题("if/else控制流" ...

  8. python 生成器 yield语句

    生成器就是一个返回迭代器(iterator)的函数. 包含了 yield 的函数,就是一个生成器. 生成器每使用yield语句产生一个值,函数就会被冻结(暂停执行),被唤醒后(即再次调用)接着上次执行 ...

  9. 6.2 C# 2:利用 yield 语句简化迭代器

    class Program { static void Main(string[] args) { object[] values = new object[] { "a", &q ...

随机推荐

  1. 浅析busybox-1.12.0中ash的脚本命令局限性

    浅析busybox-1.12.0中ash的脚本命令局限性 LUTHER= 表示将LUTHER清空,将其变为null echo ${LUTHER:-111}如果执行该句之前LUTHER变量不存在,那么显 ...

  2. scrollTo和scrollTo.js

    最近做一个项目前端要用到scrollTo和滚动视觉差.顺便把两个东西拿出来温习一下. HTML DOM里面定义了scrollTo方法,用法:scrollTo(xpos,ypos),把内容滚动到当前的指 ...

  3. Android 自己的自动化测试(4)&lt;uiautomator&gt;

    在前面的系列文章.我与介绍java实现 Android 自己主动化測试(1)怎样安装和卸载一个应用(java).Android 自己主动化測试(2)依据ID查找对象(java):然后又介绍了用pyth ...

  4. Android - 使用Intent来启动Activity

    本文地址: http://blog.csdn.net/caroline_wendy/article/details/21455141 Intent 的用途是 绑定 应用程序组件, 并在应用程序之间进行 ...

  5. Swift的基础,操作符,字符串和集合类型

    这篇文章主要讲解苹果Swift官方指南的第二章前四节的要点内容,如果想看完整的英文文档可以去苹果开发者页面下载. Basic 声明常量let 声明变量var 注释依旧使用"//" ...

  6. HDU2093 字符串2种不错的读入思路

    <span style="font-family: 'Times New Roman'; font-size: 12px; background-color: rgb(255, 255 ...

  7. hibernate某些版本(4.3)下报错 NoSuchMethodError: javax.persistence.Table.indexes()

    其实本来没啥大问题,但到网上查的时候发现了一些误人子弟的说法,所以还是记下来吧. 现象: hibernate从低版本升级到某一个版本时(我们是升到4.3.10)时,在程序启动时会报错: java.la ...

  8. Java - 文件(IO流)

    Java - 文件 (IO)   流的分类:     > 文件流:FileInputStream | FileOutputStream | FileReader | FileWriter     ...

  9. html的target用法

    _blank -- 在新窗口中打开链接 _parent -- 在父窗体中打开链接 _self -- 在当前窗体打开链接,此为默认值 _top -- 在当前窗体打开链接,并替换当前的整个窗体(框架页), ...

  10. Android使用NumberPicker控件实现选择城市,生日

    本人菜鸟一枚,第一次写技术博客,有点小紧张,加上代码很乱,求不喷. 先放效果图: 界面就这么简单. 然后放上代码: package com.example.dialog; import java.ut ...