博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
yield语句
阅读量:5271 次
发布时间:2019-06-14

本文共 4931 字,大约阅读时间需要 16 分钟。

    自C#的第一个版本以来,使用foreach语句可以轻松地迭代集合。在C#1.0中,创建枚举器仍需要做大量的工作。C#2.0添加了yield语句,以便于创建枚举器。yield return语句返回集合的一个元素,并移动到下一个元素上。yield break可停止迭代,如下例所示:
public class HelloCollection{    public IEnumerator
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属性根据位置返回一个对象。
1 public class HelloCollection 2 { 3     public IEnumerator GetEnumerator() 4     { 5         return new Enumerator(0); 6     } 7     public class Enumerator : IEnumerator
, IEnumerator, IDisposable 8 { 9 private int state;10 private string current;11 12 public Enumerator(int state) { this.state = state; }13 bool System.Collections.IEnumerator.MoveNext()14 {15 switch (state)16 {17 case 0:18 current = "Hello";19 state = 1;20 return true;21 case 1:22 current = "World";23 state = 2;24 return true;25 case 2:26 break;27 }28 return false;29 }30 void System.Collections.IEnumerator.Reset()31 {32 throw new NotSupportedException();33 }34 string System.Collections.Generic.IEnumerator
.Current35 {36 get { return current; }37 }38 object System.Collections.IEnumerator.Current39 {40 get { return current; }41 }42 void IDisposable.Dispose() { }43 }44 }
    yield return语句会生成一个枚举器,而不仅仅生成一个包含的项的列表。这个枚举器通过foreach语句调用。从foreach中依次访问每一项时,就会访问枚举器。这样就可以迭代大量的数据,而无需一次把所有的数据都读入内存。
迭代集合的不同方式
    在下面这个比Hello World示例略大但比较真实的示例中,可以使用yield return语句,以不同方式迭代集合的类。类MusicTitles 可以用默认方式通过GetEnumerator方法迭代标题,用Reverse方法逆序迭代标题,用Subset方法迭代子集:
1 public class MusicTitles 2 { 3     string[] names = { "Tubular Bells", "Hergest Ridge", "Ommadawn", "Platinum" }; 4  5     public IEnumerator
GetEnumerator() 6 { 7 for (int i = 0; i < 4; i++) 8 yield return names[i]; 9 }10 public IEnumerable
Reverse()11 {12 for (int i = 3; i >= 0; i--)13 yield return names[i];14 }15 public IEnumerable
Subset(int index, int length)16 {17 for (int i = index; i < index + length; i++)18 yield return names[i];19 }20 }
    类支持的默认迭代时定义为返回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迭代器类型。
1 public class GameMoves 2     { 3         private IEnumerator cross; 4         private IEnumerator circle; 5  6         public GameMoves() 7         { 8             cross = Cross(); 9             circle = Circle();10         }11         private int move = 0;12         const int MaxMoves = 9;13 14         public IEnumerator Cross()15         {16             while (true)17             {18                 Console.WriteLine("Cross, move {0}", move);19                 if (++move >= MaxMoves)20                     yield break;21                 yield return circle;22             }23         }24         public IEnumerator Circle()25         {26             while (true)27             {28                 Console.WriteLine("Circle, move {0}", move);29                 if (++move >= MaxMoves) yield break;30                 yield return cross;31             }32         }33     }
    在客户端程序中,可以使用如下方式使用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

转载于:https://www.cnblogs.com/ChrisLi/p/4191054.html

你可能感兴趣的文章
基于线程池的线程管理(BlockingQueue生产者消费者方式)实例
查看>>
sqlmap
查看>>
给出随机存储器(RAM)和只读存储器(ROM)的差别
查看>>
CSS3 3D Transform
查看>>
js深拷贝
查看>>
http和socket之长连接和短连接区别(转)
查看>>
【HTML】网页中如何让DIV在网页滚动到特定位置时出现
查看>>
文件序列化
查看>>
C++11 中的线程、锁和条件变量
查看>>
HDU 2485 Destroying the bus stations(!最大流∩!费用流∩搜索)
查看>>
Oracle关于用户信息的一些SQL语句
查看>>
2019-02-28处理公司同事无法上网事件记录
查看>>
HTCVive使用
查看>>
Javascript 浏览器检测
查看>>
Java程序员常用工具类库
查看>>
头文件有h和没有h的区别
查看>>
数据库的查询与视图
查看>>
洪涝有源淹没算法及淹没结果分析
查看>>
Flex在使用无线电的button切换直方图横坐标和叙述性说明
查看>>
C++ AMP 介绍(两)
查看>>