在"C#中,何時用yield return"中,咱們瞭解到:使用yield return返回集合,不是一次性加載到內存中,而是客戶端每調用一次就返回一個集合元素,是一種"按需供給"。本篇來重溫yield return的用法,探祕yield背後的故事並自定義一個能達到yield return相同效果的類,最後體驗yield break的用法。html
□ 回顧yield return的用法dom
如下代碼建立一個集合並遍歷集合。this
class Program
{static Random r = new Random();static IEnumerable<int> GetList(int count){List<int> list = new List<int>();for (int i = 0; i < count; i++){list.Add(r.Next(10));}return list;
}static void Main(string[] args){foreach(int item in GetList(5))Console.WriteLine(item);Console.ReadKey();}}
使用yield return也能得到一樣的結果。修改GetList方法爲:spa
經過斷點調試發現:客戶端每顯示一個集合中的元素,都會到GetList方法去獲取集合元素。static IEnumerable<int> GetList(int count){for (int i = 0; i < count; i++){yield return r.Next(10);
}}
□ 探密yield指針
使用yield return獲取集合,並遍歷。調試
class Program
{public static Random r = new Random();static IEnumerable<int> GetList(int count){for (int i = 0; i < count; i++){yield return r.Next(10);
}}static void Main(string[] args){foreach(int item in GetList(5))Console.WriteLine(item);Console.ReadKey();}}
生成項目,並用Reflector反編譯可執行文件。在.NET 1.0版本下查看GetList方法,發現該方法返回的是一個GetList類的實例。原來yield return是"語法糖",其本質是生成了一個GetList的實例。htm
那GetList實例是什麼呢?點擊Reflector中<GetList>連接查看。blog
○ 原來GetList類實現了IEnumerable和IEnumerator的泛型、非泛型接口
○ yield return返回的集合之因此能被迭代、遍歷,是由於GetList內部有迭代器
○ yield return之因此能實現"按需供給",是由於GetList內部有一個_state字段記錄這上次的狀態索引
接下來,就模擬GetList,咱們自定義一個GetRandomNumbersClass類,使之能達到yield return相同的效果。接口
using System;
using System.Collections;
using System.Collections.Generic;
namespace ConsoleApplication2
{class Program
{public static Random r = new Random();static IEnumerable<int> GetList(int count){GetRandomNumbersClass ret = new GetRandomNumbersClass();
ret.count = count;return ret;
}static void Main(string[] args){foreach(int item in GetList(5))Console.WriteLine(item);Console.ReadKey();}}class GetRandomNumbersClass : IEnumerable<int>, IEnumerator<int>{public int count;//集合元素的數量public int i; //當前指針private int current;//存儲當前值private int state;//保存遍歷的狀態#region 實現IEnumerator接口public int Current{get { return current; }}public bool MoveNext(){switch (state)
{case 0: //即爲初始默認值i = 0;//把指針調向0
goto case 1;break;
case 1:
state = 1;//先設置原狀態
if (!(i < count))//若是指針大於等於當前集合元素數量{return false;}current = Program.r.Next(10);state = 2; //再設置當前狀態
return true;break;
case 2: //再次遍歷若是state值爲2i++;//指針再移動一位
goto case 1;break;
}return false;}//被顯式調用的屬性
object IEnumerator.Current
{get { return Current; }}public void Reset(){throw new NotImplementedException();}public void Dispose(){}#endregion#region 實現IEnumerable的泛型和非泛型public IEnumerator<int> GetEnumerator(){return this;}//被顯式調用的屬性
IEnumerator IEnumerable.GetEnumerator(){return GetEnumerator();
}#endregion}}
關於GetRandomNumbersClass類:
○ count表示集合的長度,能夠在客戶端賦值。當調用迭代器的MoveNext方法,須要把count和當前位置比較,以決定是否能夠再向前移動。
○ 字段i至關於索引,指針每次移動一位,i須要自增1
○ current表示當前存儲的值,外部經過IEnumerator.Current屬性訪問
迭代器的MoveNext方法是關鍵:
○ state字段是整型,表示產生集合過程當中的3種狀態
○ 當state爲0的時候,說明是初始狀態,把索引位置調到0,並跳轉到state爲1的部分
○ 當state爲1的時候,首先把狀態設置爲1,而後判斷索引的位置有沒有大於或等於集合的長度,接着產生集合元素,把state設置爲2,並最終返回true
○ 當sate爲2的時候,也就是迭代器向前移動一位,再次執行MonveNext方法的時候,跳轉到state爲2的語句塊部分,把索引位置自增1,再跳轉到state爲1的語句塊中,產生新的集合元素
○ 如此循環
□ yield break的用法
假設在一個無限循環的環境中獲取一個int類型的集合,在客戶端經過某個條件來終止循環。
class Program
{static Random rand = new Random();static IEnumerable<int> GetList(){while (true){yield return rand.Next(100);
}}static void Main(string[] args){foreach (int item in GetList()){if (item%10 == 0)
{break;
}Console.WriteLine(item);}Console.ReadKey();}}
以上,當集合元素能夠被10整除的時候,就終止循環。終止循環的時機是在循環遍歷的時候。
若是用yield break,就能夠在獲取集合的時候,當符合某種條件就終止獲取集合。
class Program
{static Random rand = new Random();static IEnumerable<int> GetList(){while (true){int temp = rand.Next(100);
if (temp%10 == 0)
{yield break;
}yield return temp;
}}static void Main(string[] args){foreach (int item in GetList()){Console.WriteLine(item);}Console.ReadKey();}}
總結:○ yield return能返回一個"按需供給"的集合○ yield return是"語法糖",其背後是一個實現了IEnuerable,IEnumerator泛型、非泛型接口的類,該類維護着一個狀態字段,以保證yield return產生的集合能"按需供給"○ yield break配合yield return使用,當產生集合達到某種條件的時候使用yield break,以終止繼續建立集合