C# 中 yield return 和 yield break 关键字的用法

yield 关键字向编译器指示它所在的方法是迭代器块。 编译器生成一个类来实现迭代器块中表示的行为。 在迭代器块中,yield 关键字与 return 关键字结合使用,向枚举器对象提供值。 这是一个返回值,例如,在 foreach 语句的每一次循环中返回的值。 yield 关键字也可与 break 结合使用,表示迭代结束。 有关迭代器的更多信息,请参见迭代器(C# 编程指南) 下面的示例演示两种形式的 yield 语句。

yield return <expression>;
yield break;

在 yield return 语句中,将计算 expression 并将结果以值的形式返回给枚举器对象;expression 必须可以隐式转换为 yield 类型的迭代器。

yield break 语句中,控制权将无条件地返回给迭代器的调用方,该调用方为枚举器对象的 IEnumerator.MoveNext 方法(或其对应的泛型 System.Collections.Generic.IEnumerable<T>)或 Dispose 方法。

yield 语句只能出现在 iterator 块中,这种块可作为方法、运算符或访问器的主体实现。 这类方法、运算符或访问器的体受以下约束的控制:

  • 不允许不安全块。

  • 方法、运算符或访问器的参数不能是 ref out

  • yield return 语句不能放在 try-catch 块中的任何位置。 该语句可放在后跟 finally 块的 try 块中。

  • yield break 语句可放在 try 块或 catch 块中,但不能放在 finally 块中。

yield 语句不能出现在匿名方法中。 有关更多信息,请参见 匿名方法(C# 编程指南)

当和 expression 一起使用时,yield return 语句不能出现在 catch 块中或含有一个或多个 catch 子句的 try 块中。 有关更多信息,请参见 异常处理语句(C# 参考)

在下面的示例中,迭代器块(这里是方法 Power(int number, int power))中使用了 yield 语句。 当调用 Power 方法时,它返回一个包含数字幂的可枚举对象。 注意 Power 方法的返回类型是 System.Collections.IEnumerable(一种迭代器接口类型)。

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);
        }
    }
}
/*
Output:
2 4 8 16 32 64 128 256 
*/

迭代器是一种方法、get 访问器或运算符,它通过使用 yield 关键字对数组或集合类执行自定义迭代。 yield 返回语句会导致源序列中的元素在访问源序列中的下一个元素之前立即返回给调用方。 尽管您以方法的形式编写迭代器,但编译器会将其转换为一个实际上是状态机的嵌套类。 只要客户端代码中的 foreach 循环继续进行,此类就会跟踪迭代器的位置。

注意注意

若要了解编译器在后台执行了什么操作,请使用 ILDASM.exe 工具来查看为迭代器方法生成的中间语言 (IL) 代码。

将使用 foreach 语句从客户端代码中调用迭代器。 例如,您可以为类创建一个迭代器,该迭代器将按相反顺序返回元素,或在迭代器返回元素之前对每个元素执行操作。 在为结构创建迭代器时,您不必实现整个 IEnumerator 接口。 当编译器检测到迭代器时,它将自动生成 IEnumeratorIEnumerator<T> 接口的 CurrentMoveNextDispose 方法。

  • 迭代器是可以返回相同类型的值的有序序列的一段代码。

  • 迭代器可用作方法、运算符或 get 访问器的代码体。

  • 迭代器代码使用 yield return 语句依次返回每个元素。 yield break 将终止迭代。

  • 可以在类中实现多个迭代器。 每个迭代器都必须像任何类成员一样有唯一的名称,并且可以在 foreach 语句中被客户端代码调用,如下所示:foreach(int x in SampleClass.Iterator2){}

  • 迭代器的返回类型必须为 IEnumerableIEnumeratorIEnumerable<T>IEnumerator<T>

  • 迭代器是 LINQ 查询中延迟执行行为的基础。

yield 关键字用于指定返回的一个或多个值。 到达 yield return 语句时,会保存当前位置。 下次调用迭代器时将从此位置重新开始执行。

迭代器对集合类特别有用,它提供一种简单的方法来迭代复杂的数据结构(如二进制树)。

在本示例中,DaysOfTheWeek 类是将一周中的各天作为字符串进行存储的简单集合类。 foreach 循环每迭代一次,都返回集合中的下一个字符串。

public class DaysOfTheWeek : System.Collections.IEnumerable
{
    string[] days = { "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat" };

    public System.Collections.IEnumerator GetEnumerator()
    {
        for (int i = 0; i < days.Length; i++)
        {
            yield return days[i];
        }
    }
}

class TestDaysOfTheWeek
{
    static void Main()
    {
        // Create an instance of the collection class
        DaysOfTheWeek week = new DaysOfTheWeek();

        // Iterate with foreach
        foreach (string day in week)
        {
            System.Console.Write(day + " ");
        }
    }
}
// Output: Sun Mon Tue Wed Thr Fri Sat

 
    ★IEnumerator对象:
        public IEnumerator<int> GetEnumerator()//注意方法名
        {
            yield return 0;
            yield return 1;
            yield return 2;
            yield return 3;
        }
可以作用于foreach语句,但是方法所在的类用在foreach语句中,并且必须使用GetEnumerator作为获取迭代器的方法名。
        Integers ints = new Integers();//Integers是GetEnumerator()方法所在的类。
        foreach (int i in ints)
        {
            Console.WriteLine(i.ToString());
        }
 
PS:几乎所有的对yield return和直接return list的测试结果都会告诉你,yield return更慢。但是当你真正了解yield之后,相信多数情况下都会选择yield return的。
 
今天项目中用到了 yield return ,主要是为了实现 延迟迭代的需求,所以就写了这个小的 Demo,希望对你有帮助! 代码如下:
using System;
using System.Collections.Generic;
  
namespace ConAppYield
{
    class Program
    {
        static void Main(string[] args)
        {
            // Yield 的好处是 延迟遍历,只有在遍历具体的数据时才去执行对应的代码,Linq 中大量的扩展方法用到了,比如:select 等等
            Console.WriteLine("==偶数年龄如下:==");
            IEnumerable<int> query = Students.GetEvenNumbers();
            foreach (var item in query)
            {
                Console.WriteLine(item);
            }
              
              
            Console.WriteLine("==下面用 do while 模拟 C# 中的 foreach 循环,说到底,foreach 循环只是 C# 的语法糖而已! ==");
            Console.WriteLine("==同时,foreach 循环也是一种设计模式,叫 迭代模式==");
            IEnumerator<int> newQuery = query.GetEnumerator();
            int i = 0;
            while(newQuery.MoveNext())
            {
                i++;
                Console.WriteLine("集合中的第 "+ i +" 个数:" + newQuery.Current);
            }
            Console.ReadKey();
        }
    }
  
    class Students
    {
        private static readonly int[] ages = new int[] { 1,2,3,4,5,6,7,8,9,10 };
  
        public static IEnumerable<int> GetEvenNumbers()
        {
            int agesLength = ages.Length;
            for (int i = 0; i < agesLength; i++)
            {
                if (ages[i] % 2 == 0)
                {
                    yield return ages[i];
                }
                else
                {
                    //yield break; //yield break 可以终止循环,即:只要碰到的数不是偶数,则立即终止循环
                }
            }
            /* 下面的写法错误:无法在包含 catch 子句的 Try 块体中生成值 */
            /*
            try
            {
                foreach (var item in ages)
                {
                    if (item % 2 == 0)
                    {
                        yield return item;
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }*/
  
        }
    }
}
谢谢浏览!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章