晚上好,各位。今天結合書中所講和MSDN所查,聊下yield關鍵字,它是我們簡化迭代器的關鍵。
如果你在語句中使用了yield關鍵字,則意味著它在其中出現的方法、運算符或get訪問器是迭代器,通過使用yield定義迭代器,可在實現自定義集合類型的IEnumerable和IEnumerator模式時無需顯示類(保留枚舉狀態類),使用yield有兩種形式,如下
1 yield return 表達式 2 yield break
先說明一下yield return語句,每一次返回一個元素。通過foreach語句或LINQ查詢來使用迭代器方法。foreach循環的每次迭代都會調用迭代器方法,會保存當前返回的狀態。
1 static void Main(string[] args) 2 { 3 foreach (int i in GetValues()) 4 { 5 Console.WriteLine(i); 6 } 7 Console.ReadKey(); 8 } 9 10 static IEnumerable<int> GetValues() 11 { 12 yield return 1; 13 yield return 2; 14 yield return 3; 15 }
不能將yield return語句放在try-catch之中,但可以把它放在try-finally之中。yield return有兩個特點:1.返回類型必須是IEnumerable、IEnumerator、IEnumerator<T>、IEnumerable<T>.2.不能使用ref和out修飾符。在匿名方法和不安全代碼中不能包含yield return和yield break。下面我們使用yield return來簡化上一篇中對Student的迭代,重新個性了Queue<T>這個泛型類,如下
1 class Queue<T> : IEnumerable<T> where T : class 2 { 3 public List<T> objects = new List<T>(); 4 int startPoint = 0; 5 public Queue(List<T> list) 6 { 7 objects = list; 8 } 9 10 //實現從IEnumerable中的GetEnumerator方法 11 /* 12 個人覺得這個方法在迭代中只會調用一次,不然每次都返回一個新的QueueIterator<T>對象,位置記錄都會重置為-1 13 */ 14 public IEnumerator<T> GetEnumerator() 15 { 16 //return new QueueIterator<T>(this); 17 for (int index = 0; index < objects.Count; index++) 18 { 19 20 yield return objects[(index + startPoint) % objects.Count]; 21 } 22 } 23 24 IEnumerator IEnumerable.GetEnumerator() 25 { 26 throw new NotImplementedException(); 27 } 28 }
代碼到了yield return時,返回objects中一個合適的元素,並保存當前的狀態,等下一次調用時從記數的位置開始。在使用程序中,如下,和原先一樣。
1 List<Student> list = new List<Student> { 2 new Student("СA"), 3 new Student("СB"), 4 new Student("СC"), 5 new Student("СD"), 6 new Student("СE") 7 }; 8 ConsoleDemo.Chapter6.Queue<Student> lq = new Chapter6.Queue<Student>(list); 9 10 foreach (var obj in lq) 11 { 12 obj.SayName(); 13 }
可以看到結果都是迭代的打印每一個元素的名字。在來說下return break,從break中可以看中,是跳出,那意思就應該上跳出迭代,如
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 6 foreach (int i in GetValues()) 7 { 8 Console.WriteLine(i); 9 } 10 Console.ReadKey(); 11 } 12 13 static IEnumerable<int> GetValues() 14 { 15 yield return 1; 16 yield return 2; 17 yield break; 18 yield return 3; 19 } 20 }
到了yield return 2時,下一語句是yield break,則在控制台只會打印1,2,因為在打印3之前就使用yield break跳出迭代了。
下面我們使用一個實際點的讀文件的例子。新建一個文本demo.txt,內容如下
1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 0
代碼如下
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 foreach(var line in ReadLines("./demo.txt")) 6 { 7 Console.WriteLine(line); 8 } 9 Console.ReadKey(); 10 } 11 12 static IEnumerable<string> ReadLines(string path) 13 { 14 string line; 15 TextReader tr = File.OpenText(path); 16 while ((line = tr.ReadLine()) != null) 17 { 18 yield return line; 19 } 20 } 21 }
當然其實File中的表態方法是有ReadLines方法的,返回的也是IEnumerable<string>,看來我們是多此一舉了,不過也不錯,知道了一些yield的使用,原理的那些真心不敢寫,寫了自己也看不懂,希望以後能用自己組織語言,解譯那些原理。
請斧正。