zoukankan      html  css  js  c++  java
  • yield语句

        自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属性根据位置返回一个对象。
     1 public class HelloCollection
     2 {
     3     public IEnumerator GetEnumerator()
     4     {
     5         return new Enumerator(0);
     6     }
     7     public class Enumerator : IEnumerator<string>, 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<string>.Current
    35         {
    36             get { return current; }
    37         }
    38         object System.Collections.IEnumerator.Current
    39         {
    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<string> GetEnumerator()
     6     {
     7         for (int i = 0; i < 4; i++)
     8             yield return names[i];
     9     }
    10     public IEnumerable<string> Reverse()
    11     {
    12         for (int i = 3; i >= 0; i--)
    13             yield return names[i];
    14     }
    15     public IEnumerable<string> 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
  • 相关阅读:
    win10 uwp 弹起键盘不隐藏界面元素
    win10 uwp 存放网络图片到本地
    win10 uwp 存放网络图片到本地
    sublime Text 正则替换
    sublime Text 正则替换
    win10 uwp 绘图 Line 控件使用
    win10 uwp 绘图 Line 控件使用
    AJAX 是什么?
    什么是 PHP SimpleXML?
    PHP XML DOM:DOM 是什么?
  • 原文地址:https://www.cnblogs.com/ChrisLi/p/4191054.html
Copyright © 2011-2022 走看看