代码改变世界

IEnumerable、IEnumerator与yield的学习

2015-04-16 13:05  阿诚de窝  阅读(1030)  评论(0编辑  收藏  举报

我们知道数组对象可以使用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里的变种——协程