晚上好,各位。今天结合书中所讲和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>对象,位置记录都会重置为-113 */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 910 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的使用,原理的那些真心不敢写,写了自己也看不懂,希望以后能用自己组织语言,解译那些原理。
请斧正。
- 2楼Slark.NET
- 学习了
- 1楼FerventDesert
- 楼主可以看一下我这篇文章,http://www.cnblogs.com/buptzym/p/4141539.html
- Re: a2htray
- @FerventDesert,引用楼主可以看一下我这篇文章,http://www.cnblogs.com/buptzym/p/4141539.html,还是你比较深入,我这篇可能连入门也不算,非常感谢!