包含不安全的块的方法。 有关详细信息,请参阅unsafe(C# 参考)。
2)返回类型要求:执行一次会将控制权从函数里面交出,给外部,如果外部是异步的或者子线程一直循环的被挂起了,下次再重入该函数,那么该yield函数的调用堆栈状态会被保留,从而接着上次状态继续执行。
再重新进来不会保留堆栈信息,也就和一般迭代集合返回一样了。
foreach语句
C#中的foreach语句不会翻译为IL中的foreach语句,而是会翻译为IEnumerable中的接口的属性和函数,将类型替换为相应的泛型,IEnumerator<T> while MoveNext Current语句,会用while一次遍历迭代器的所有元素。foreach语句会调用类型的GetEnumerator方法。
yield语句也是一个迭代块,必须声明返回值类型为IEnumerable、IEnumerable<T>、IEnumerator<T>、IEnumerator类型。yield语句会生成一个枚举器IEnumerator,CRL翻译为Switch语句的MoveNext、Current语句、State保持当前的语句下一个索引状态的状态迭代器,下次重入的时候就从迭代器的下一个索引取数据并置下一个状态,就马上返回,就是一个很简单程序设置没有什么高级的分段执行机制,在异步线程或者协程事件回调中才会赋予它保留现场继续执行的强大能力。
验证实例代码:
public class PowersOf2 { static void Main() { // 1.返回类型,用于查询返回一个,不是一次函数执行到底提高了线性遍历性能 // 返回Oject没有什么作用 //Object enum_Num = Power(2, 8); //Console.Write("{0} ", enum_Num); // 返回IEnumerable<T>,返回时候要隐式转换为泛型具体类型,且外部遍历也要foreach驱动,因为里面只取一个返回。 //Display powers of 2 up to the exponent of 8: // 2.yield break 会不等全部遍历完而返回,可以限定返回值区间 bool bFind = false; int nFindValueExist = 64; foreach (int i in Power(2, 8)) { // 查询存在 if (i == nFindValueExist) { bFind = true; break; } if( i == 2 ) { break; } } foreach (int i in Power(2, 8)) { // 查询存在 if (i == nFindValueExist) { bFind = true; break; } } if( bFind ) { Console.Write("Find {0} OK", nFindValueExist); } else { Console.Write("Find {0} Failed", nFindValueExist); } } public static IEnumerable Power(int number, int exponent)//<int> { int result = 1; for (int i = 0; i <= exponent; i++) { if (i == 0) { yield return result; } else if( i < 5 ) { result = result * number; yield return result; } else { yield break; } } } // Output: 1 2 4 8 16 32 64 128 256 }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TestYield { class Program { static IEnumerable<int> WithNoYield() { IList<int> list = new List<int>(); for (int i = 0; i < 20; i++) { Console.WriteLine("Quary " + i.ToString()); if (i > 2) list.Add(i); } return list; } static IEnumerable<int> WithYield() { for (int i = 0; i < 20; i++) { Console.WriteLine("Quary " + i.ToString()); if (i > 2) yield return i; } } static void Main(string[] args) { Console.WriteLine("------------------ WithNoYield--------------"); WithNoYield(); Console.WriteLine("------------------WithYield--------------"); // 直接调用yield封装的函数不会被执行,会延迟计算 WithYield(); WithYield(); WithYield(); WithYield(); WithYield(); WithYield(); // 执行一次会将控制权从函数里面交出,给外部,如果外部是异步的或者子线程一直循环的被挂起了 // 下次再重入该函数,那么该yield函数的调用堆栈状态会被保留,从而接着上次状态继续执行 Console.WriteLine("------------------ foreach WithNoYield--------------"); foreach (int i in WithNoYield()) { Console.WriteLine("Result " + i.ToString()); } // 如果yield迭代器全部执行完了,再重新进来不会保留堆栈信息 Console.WriteLine("------------------ foreach WithYield1--------------"); foreach (int i in WithYield()) { Console.WriteLine("Result " + i.ToString()); if( i == 10 ) { break; } } Console.WriteLine("------------------ foreach WithYield2-------------"); foreach (int i in WithYield()) { Console.WriteLine("Result " + i.ToString()); } Console.ReadLine(); } } }
yield语句和foreach语句的枚举访问器展开特性验证:
using System; using System.Collections; using System.Linq; using System.Text; namespace Wrox.ProCSharp.Arrays { public class GameMoves { // 两个迭代器,每个都有自己的While,Current, MoveNext调用迭代器自己,yield语句还会导致记录迭代器state状态。 private IEnumerator cross; private IEnumerator circle; public GameMoves() { // 迭代器就是一个返回枚举集合的方法,这里已经声明了,这里两个是固定的只是外部的会切换 cross = Cross2(); circle = Circle(); } private int move = 0; const int MaxMoves = 9; // 迭代器MoveNext会进来这里,也就是进来迭代器本身遍历获取对象 public IEnumerator Cross2() { while (true) { Console.WriteLine("Cross, move {0}", move); if (++move >= MaxMoves) yield break; yield return circle; } } public IEnumerator Circle() { while (true) { Console.WriteLine("Circle, move {0}", move); if (++move >= MaxMoves) yield break; yield return cross; } } } }
static void Main() { // 只是声明不会调用 var game = new GameMoves(); // 外部的迭代器类型声明为Cross2,声明时候只是声明不会调用 IEnumerator enumerator = game.Cross2(); // 会进入枚举器 // 只是调用当前迭代器,当前迭代器返回值赋值给了当前的enumerator.Current,值是另外一个迭代器 while (enumerator.MoveNext()) { // 用另外迭代器换掉当前迭代器 enumerator = enumerator.Current as IEnumerator; } }