yield 关键字和迭代器

一般使用方法
 
 
yield 关键字向编译器指示它所在的方法是迭代器块
在迭代器块中,yield 关键字与 return 关键字结合使用,向枚举器对象提供值。 这是一个返回值,例如,在
foreach 语句的每一次循环中返回的值。 yield 关键字也可与 break 结合使用,表示迭代结束。
 
不允许不安全块。
 
方法、运算符或访问器的参数不能是 ref 或 out。
 
yield return 语句不能放在 try-catch 块中的任何位置。 该语句可放在后跟 finally 块的 try 块中。
 
yield break 语句可放在 try 块或 catch 块中,但不能放在 finally 块中。
yield 语句不能出现在匿名方法中
 
Yield的执行方法
调用函数
调用者请求item
下一个item返回
回到步骤2
 
 
yield不能单独放在try-catch块中,如果try中有yield那么,这个try块后面不许跟着finally块;也不能出现在匿名
方法中,所以,看起来yield似乎并不常用,但是也不是不用。我前面有一个关于迭代器的例子《C#中的迭代器基础
》中就用到了。可以参考一下那个例子,但是这里要再说的一点是我后来看到的,yield是跟return一起使用的,形
式为yield return xxx,一般来说单独的return在每个方法中只能存在一个。而yield则不同的是,可以出现连续多
个。
迭代器,是一个连续的集合,出现多个yield return其实就是将这多个的yield return元素按照出现的顺序存储在迭
代器的集合中而已
static IEnumerable<Int32> CountWithTimeLimit(DateTime limit)
{
    try
    {
        for (int i = 1; i <= 100; i++)
        {
            if (DateTime.Now >= limit)
            {
                yield break;
            }
            yield return i;
        }
    }
    finally
    {
        Console.WriteLine("停止迭代!"); Console.ReadKey();
    }
}
static void Main(string[] args)
{
    DateTime stop = DateTime.Now.AddSeconds(2);
    foreach (Int32 i in CountWithTimeLimit(stop))
    {
        Console.WriteLine("返回 {0}", i);
        Thread.Sleep(300);
    }
}
 
简单几行代码就能够完全实现IterationSampleIterator类所需要的功能。方法看起来很普通,除了使用了yield
return。这条语句告诉编译器这不是一个普通的方法,而是一个需要执行的迭代块(yield block),他返回一个
IEnumerator对象,你能够使用迭代块来执行迭代方法并返回一个IEnumerable需要实现的类型,IEnumerator或者对
应的泛型。如果实现的是非泛型版本的接口,迭代块返的yield type是Object类型,否则返回的是相应的泛型类型。
例如,如果方法实现IEnumerable<String>接口,那么yield返回的类型就是String类型。 在迭代块中除了yield
return外,不允许出现普通的return语句。块中的所有yield return 语句必须返回和块的最后返回类型兼容的类型
。举个例子,如果方法定义需要返回IEnumeratble<String>类型的话,不能yield return 1 。 需要强调的一点是,
对于迭代块,虽然我们写的方法看起来像是在顺序执行,实际上我们是让编译器来为我们创建了一个状态机。这就是
在C#1中我们书写的那部分代码---调用者每次调用只需要返回一个值,因此我们需要记住最后一次返回值时,在集合
中位置。 当编译器遇到迭代块是,它创建了一个实现了状态机的内部类。这个类记住了我们迭代器的准确当前位置
以及本地变量,包括参数。这个类有点类似与我们之前手写的那段代码,他将所有需要记录的状态保存为实例变量。
下面来看看,为了实现一个迭代器,这个状态机需要按顺序执行的操作:
 
它需要一些初始的状态
当MoveNext被调用时,他需要执行GetEnumerator方法中的代码来准备下一个待返回的数据。
当调用Current属性是,需要返回yielded的值。
需要知道什么时候迭代结束是,MoveNext会返回false
 
class Program
{
    static readonly String Padding = new String(' ', 30);
    static IEnumerable<Int32> CreateEnumerable()
    {
        Console.WriteLine("{0} CreateEnumerable()方法开始", Padding);
        for (int i = 0; i < 3; i++)
        {
            Console.WriteLine("{0}开始 yield {1}", i);
            yield return i;
            Console.WriteLine("{0}yield 结束", Padding);
        }
        Console.WriteLine("{0} Yielding最后一个值", Padding);
        yield return -1;
        Console.WriteLine("{0} CreateEnumerable()方法结束", Padding);
    }
 
    static void Main(string[] args)
    {
        IEnumerable<Int32> iterable = CreateEnumerable();
        IEnumerator<Int32> iterator = iterable.GetEnumerator();
        Console.WriteLine("开始迭代");
        while (true)
        {
            Console.WriteLine("调用MoveNext方法……");
            Boolean result = iterator.MoveNext();
            Console.WriteLine("MoveNext方法返回的{0}", result);
            if (!result)
            {
                break;
            }
            Console.WriteLine("获取当前值……");
            Console.WriteLine("获取到的当前值为{0}", iterator.Current);
        }
        Console.ReadKey();
    }
}
 
------------------------------------------------------------------------------------------
public class List
{
    //using System.Collections;
    public static IEnumerable Power(int number, int exponent)
    {
        int counter = 0;
        int result = 1;
        while (counter++ < exponent)
        {
            result = result * number;
            yield return result;
        }
    }
 
    static void Main()
    {
        // Display powers of 2 up to the exponent 8:
        foreach (int i in Power(2, 8))
        {
            Console.Write("{0} ", i);
        }
    }
}
原文地址:https://www.cnblogs.com/One-dream-man/p/3500684.html