我们知道数组对象可以使用foreach迭代进行遍历,同时我们发现类ArrayList和List也可以使用foreach进行迭代。如果我们自己编写的类也需要使用foreach进行迭代时该怎么办呢?
IEnumerable:
1 public interface IEnumerable 2 { 3 IEnumerator GetEnumerator(); 4 }
如果自己编写的类需要foreach进行迭代就需要实现IEnumerable接口,表示当前的类可以进行迭代。
我们发现该接口唯一的方法返回的是另一个接口IEnumerator,下面看看这个接口是干嘛的。
IEnumerator:
1 public interface IEnumerator 2 { 3 object Current { get; } 4 bool MoveNext(); 5 void Reset(); 6 }
如果说IEnumerable接口是表示当前类可以进行迭代,那么IEnumerator则是实现迭代逻辑的接口,我们需要编写一个实现IEnumerator接口的类并在其中编写好迭代逻辑。
下面直接上一个例子:
People.cs:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Test 8 { 9 /// <summary> 10 /// 自定义的可迭代类. 11 /// </summary> 12 class People : IEnumerable<Person> 13 { 14 //这里用了一个 List 有点无聊, 因为 List 本身就可以进行迭代, 为了写例子没办法 15 private List<Person> _list; 16 17 public People() 18 { 19 _list = new List<Person>(); 20 } 21 22 public IEnumerator<Person> GetEnumerator() 23 { 24 return new PeopleEnumerator(_list.ToArray()); 25 } 26 27 //示例程序所以这里就添加一个方法就行了 28 public void AddPerson(Person person) 29 { 30 _list.Add(person); 31 } 32 33 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 34 { 35 return this.GetEnumerator(); 36 } 37 } 38 39 /// <summary> 40 /// 迭代器的逻辑实现类. 41 /// </summary> 42 class PeopleEnumerator : IEnumerator<Person> 43 { 44 public Person[] pers; 45 46 private int index = -1; 47 48 public PeopleEnumerator(Person[] pers) 49 { 50 this.pers = pers; 51 } 52 53 public Person Current 54 { 55 get 56 { 57 return pers[index]; 58 } 59 } 60 61 public bool MoveNext() 62 { 63 index++; 64 return index < pers.Length; 65 } 66 67 public void Reset() 68 { 69 index = -1; 70 } 71 72 public void Dispose() 73 { 74 pers = null; 75 } 76 77 object System.Collections.IEnumerator.Current 78 { 79 get { return Current; } 80 } 81 } 82 83 /// <summary> 84 /// 集合的元素. 85 /// </summary> 86 class Person 87 { 88 public string name; 89 public bool isMale; 90 91 public Person(string name, bool isMale) 92 { 93 this.name = name; 94 this.isMale = isMale; 95 } 96 } 97 }
Program.cs:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Test 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 new Program(); 14 15 Console.ReadKey(); 16 } 17 18 public Program() 19 { 20 People people = new People(); 21 people.AddPerson(new Person("tony", true)); 22 people.AddPerson(new Person("tony mom", false)); 23 people.AddPerson(new Person("alen", true)); 24 people.AddPerson(new Person("gilbret", true)); 25 people.AddPerson(new Person("mark", false)); 26 27 foreach(Person person in people) 28 { 29 Console.WriteLine("Name: {0}, sex is male:{1}", person.name, person.isMale); 30 } 31 } 32 } 33 }
下面是运行结果:
1 Name: tony, sex is male:True 2 Name: tony mom, sex is male:False 3 Name: alen, sex is male:True 4 Name: gilbret, sex is male:True 5 Name: mark, sex is male:False
yield:
yield 是 C# 提供的一个特殊的用于迭代的语法,其可以简化迭代实现的代码,yield return 语句返回集合的一个元素,并移动到下一个元素上,yield break 可以停止迭代。
头晕了吧?没关系,我们先看看一个简单的例子:
1 using System; 2 using System.Collections; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Test 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 new Program(); 15 16 Console.ReadKey(); 17 } 18 19 public Program() 20 { 21 People people = new People(); 22 23 foreach(string name in people) 24 { 25 Console.WriteLine(name); 26 } 27 } 28 } 29 30 class People : IEnumerable 31 { 32 public IEnumerator GetEnumerator() 33 { 34 yield return "gilbert"; 35 yield return "alen"; 36 yield return "grace"; 37 } 38 } 39 }
运行的结果为:
1 gilbert 2 alen 3 grace
没错,当程序碰到yield return这个语句时就将其后面附带的数据作为current返回,同时程序会再此处暂停,运行结束foreach中的代码后再继续,同时执行的是下一个语句了,我们再看看yield break的效果,该效果表示立即停止迭代,示例如下:
1 using System; 2 using System.Collections; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Test 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 new Program(); 15 16 Console.ReadKey(); 17 } 18 19 public Program() 20 { 21 People people = new People(); 22 23 foreach(string name in people) 24 { 25 Console.WriteLine(name); 26 } 27 } 28 } 29 30 class People : IEnumerable 31 { 32 public IEnumerator GetEnumerator() 33 { 34 yield return "gilbert"; 35 yield return "alen"; 36 yield break;//指示这里要停止迭代 37 yield return "grace"; 38 } 39 } 40 }
运行的结果为:
1 gilbert 2 alen
最后要说一下:包含yoeld语句的方法或者属性也称为迭代块,迭代块必须声明为返回IEnumerator或IEnumerable接口,迭代块可以包含多个yield return或yield break语句,但是不能包含return语句。
不要小看yield迭代快,下一篇笔记我要可转回U3D了,我们要详细的看看yield在U3D里的变种——协程。