5.6.3 yield语句
C# 1.0使用foreach语句可以轻松地迭代集合。在C# 1.0中,创建枚举器仍需要做大量的工作。C# 2.0添加了yield语句,以便于创建枚举器。
yield return语句返回集合的一个元素,并移动到下一个元素上。yield break可停止迭代。
下面的例子是用yield return语句实现一个简单集合的代码。类HelloCollection包含GetEnumerator()方法。该方法的实现代码包含两个yield return语句,它们分别返回字符串Hello和World。
using System;
using System.Collection;
namespace Wrox.ProCAharp.Arrays
{
public class HelloCollection
{
public IEumerator GetEumerator()
{
yield return "Hello";
yield return "World";
}
}
}
|
警告:
包含yield语句的方法或属性也称为迭代块。迭代块必须声明为返回IEnumerator或IEnumerable接口。这个块可以包含多个yield return语句或yield break语句,但不能包含return语句。
现在可以用foreach语句迭代集合了:
public class Program
{
HelloCollection helloCollection = new HelloCollection();
foreach (string s in helloCollection)
{
Console.WriteLine(s);
}
}
}
|
使用迭代块,编译器会生成一个yield 类型,其中包含一个状态机,如下面的代码所示。yield 类型执行IEnumerator和IDisposable接口的属性和方法。在下面的例子中,可以把yield 类型看作内部类Enumerator。外部类的GetEnumerator()方法实例化并返回一个新的yield 类型。在yield 类型中,变量state定义了迭代的当前位置,每次调用MoveNext()时,当前位置都会改变。MoveNext()封装了迭代块的代码,设置了current变量的值,使Current属性根据位置返回一个对象。
public class HelloCollection
{
public IEnumerator GetEnumerator()
{
Enumerator enumerator = new Enumerator();
return enumerator;
}
public class Enumerator : IEnumerator, IDisposable
{
private int state;
private object current;
public Enumerator(int state)
{
this.state = state;
}
bool System.Collections.IEnumerator.MoveNext()
{
switch (state)
{
case 0:
current = "Hello";
state = 1;
return true;
case 1:
current = "World";
state = 2;
return true;
case 2:
break;
}
return false;
}
void System.Collections.IEnumerator.Reset()
{
throw new NotSupportedException();
}
object System.Collections.IEnumerator.Current
{
get
{
return current;
}
}
void IDisposable.Dispose()
{
}
}
}
|
现在使用yield return语句,很容易实现允许以不同方式迭代集合的类。类MusicTitles可以用默认方式通过GetEnumerator()方法迭代标题,用Reverse()方法逆序迭代标题,用Subset()方法搜索子集:
public class MusicTitles
{
string[] names = {
"Tubular Bells", "Hergest Ridge",
"Ommadawn", "Platinum");
public IEnumerator GetEnumerator()
{
for (int i = 0; i < 4; i++)
{
yield return names[i];
}
}
public IEnumerable Reverse()
{
for (int i = 3; i >= 0; i–)
{
yield return names[i];
}
}
public IEnumerable Subset( int index, int length)
{
for (int i = index; i < index + length; i++)
{
yield return names[i];
}
}
}
|
迭代字符串数组的客户代码先使用GetEnumerator()方法,该方法不必在代码中编写,因为这是默认使用的方法。然后逆序迭代标题,最后将索引和要迭代的元素个数传送给Subset()方法,来迭代子集:
MusicTitles titles = new MusicTitles();
foreach(string title in titles)
{
ConsoleWriteLine(title);
}
ConsoleWriteLine();
ConsoleWriteLine("reverse");
foreach(string title in titles.Reverse())
{
ConsoleWriteLine(title);
}
ConsoleWriteLine();
ConsoleWriteLine("subset");
foreach(string title in titles.Subset(2, 2))
{
ConsoleWriteLine(title);
}
|
使用yield语句还可以完成更复杂的任务,例如从yield return中返回枚举器。
在TicTacToe游戏中有9个域,玩家轮流在这些域中放置“十”字或一个圆。这些移动操作由GameMoves类模拟。方法Cross()和Circle()是创建迭代类型的迭代块。变量cross和circle在GameMoves类的构造函数中设置为Cross()和Circle()方法。这些域不设置为调用的方法,而是设置为用迭代块定义的迭代类型。在Cross()迭代块中,将移动操作的信息写到控制台上,并递增移动次数。如果移动次数大于9,就用yield break停止迭代;否则,就在每次迭代中返回yield类型cross的枚举对象。Circle()迭代块非常类似于Cross()迭代块,只是它在每次迭代中返回circle迭代类型。
public calss GameMoves
{
private IEnumerator cross;
private IEnumerator circle;
public GameMoves()
{
cross = Cross();
circle = Circle();
}
private int move = 0;
public IEnumerator Cross()
{
while (true)
{
Console.WriteLine("Cross, move {0}", move);
move++;
if (move > 9)
yield break;
yield return circle;
}
}
public IEnumerator Circle()
{
while (true)
{
Console.WriteLine("Circle, move {0}", move);
move++;
if (move > 9)
yield break;
yield return cross;
}
}
}
|
在客户程序中,可以以如下方式使用GameMoves类。将枚举器设置为由game.Cross()返回的枚举器类型,以设置第一次移动。enumerator.MoveNext调用以迭代块定义的一次迭代,返回另一个枚举器。返回的值可以用Current属性访问,并设置为enumerator变量,用于下一次循环:
GameMoves game = new GameMoves();
IEnumerator enumerator = game.Cross();
while (enumerator.MoveNext())
{
enumerator = (IEnumerator) enumerator.Current;
}
|
这个程序的输出会显示交替移动的情况,直到最后一次移动:
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
| 回书目 上一节 下一节 |
|
· 第六章 你能帮我吗?.. · Linux笔试面试题选摘测.. · 08年5月软考网管上午真.. · 性能测试从零开始 目录 · 08年5月软考网工上午真.. · 上周拒绝服务攻击(DDo.. |
· 08年5月各大网上书店及.. · 2008年5月24日软考试题.. · 软件设计师专家临考模.. · 上周网络管理员专家自.. · 网络工程师自测获奖名.. · 08年4月各大网上书店及.. |
|
||||
| · NAC安全访问控制 · 网络布线测试仪器 · Windows Server 2008专.. · Windows远程桌面应用 · 网络故障排除宝典 · 运营商封堵ADSL共享 中.. · 解析35岁技术人的价值.. · 世纪枭雄比尔盖茨的王.. |
· 主流品牌防火墙配置 · ASP.NET开发教程 · 超级计算机TOP500专题 · Vista SP1对决XP SP3 · SQL Server 2008/2005.. · 程序员如何成长? · C#技术开发指南 · 虚拟化技术还有点“虚” |
|||
|
||||
| · SOA 面向服务架构 · SQL Server 2008/2005.. · Apache技术专题 · 三层交换技术专题 · SQL Server入门到精通 · Windows远程桌面应用 · C#技术开发指南 · Apache技术专题 |
· Windows集群服务应用 · C#技术开发指南 · 国际文档格式标准开战 · 路由器设置与口令恢复 · Linux 集群技术专题 · PHP开发应用手册 · SOA 面向服务架构 · 企业数据恢复指南 |
|||
|
||||
| · SQL Server入门到精通 · SQL Server 2008/2005.. · SOA 面向服务架构 · Apache技术专题 · C#技术开发指南 · 三层交换技术专题 · Apache技术专题 · C#技术开发指南 |
· Windows远程桌面应用 · 企业数据恢复指南 · Windows集群服务应用 · 路由器设置与口令恢复 · Linux 集群技术专题 · SOA 面向服务架构 · 了解统一威胁管理(UTM).. · 反垃圾邮件技术应用 |
|||