在自己的对象里实现IEnumerator和IEnumerable

平时工作中我们经常用foreach来迭代一个集合。比如

1 foreach (Student student in myClass)
2 3        Console.WriteLine(student);
4 5  

基本所有的集合都能够foreach,但是必须要实现IEnumerable接口。IEnumerable接口很简单,就只有一个IEnumerator GetEnumerator() 方法。看这个方法的定义就知道,仅仅是公开了另一个接口IEnumerator。而IEnumerator才是真正的支持一个集合的迭代。IEnumerator有1个属性和2个方法。

public object Current;

public void Reset();

public bool MoveNext();

只允许读取集合的数据,而不允许修改。为了详细的讲解,我们来写一个简单的例子,就会一目了然。

首先我们创建一个学生类Student如下:

 

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 
 6 namespace IenumerableDemo
 7 {
 8     public class Student
 9     {
10         #region 私有变量
11 
12         private readonly string _id;
13         private string _firstname;
14         private string _lastname;
15 
16         #endregion
17         #region 属性
18         public string ID { get { return _id; } }
19 
20         public string FirstName { get { return _firstname; } set { _firstname = value; } }
21 
22         public string LastName { get { return _lastname; } set { _lastname = value; } }
23 
24 
25         #endregion
26 
27         #region 构造函数
28 
29         public Student(string id, string firstname, string lastname)
30         {
31             this._id = id;
32 
33             this._firstname = firstname;
34 
35             this._lastname = lastname;
36         }
37 
38         #endregion
39         #region  重写基类object方法
40 
41         public override string ToString()
42         {
43             return string.Format("{0} {1},ID:{2}", _firstname, _lastname, _id);
44         }
45 
46         public override bool Equals(object obj)
47         {
48             if (obj == null) return false;
49             if (Object.ReferenceEquals(this, obj)) return true;
50             if (this.GetType() != obj.GetType()) return false;
51 
52             Student objstudent = (Student)obj;
53             if (_id.Equals(objstudent._id)) return true;
54 
55             return false;
56         }
57 
58         public override int GetHashCode()
59         {
60             return _id.GetHashCode();
61         }
62         #endregion
63 
64     }
65 }
66 
67         

接下来我们定义一个ClassList类来承载学生。让我们先忘记Ienmerable。这个类包含一个ArrayList字段_student,在构造函数中模拟3个学生。_student是私有的,不对外公开的。

 

 1 using System;
 2 using System.Collections;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6 
 7 namespace IenumerableDemo
 8 {
 9     public class ClassList
10     {
11 
12         #region private Members
13 
14         private readonly string _id;
15 
16         private ArrayList _students;
17 
18         #endregion
19 
20         #region Properties
21         public string ID { get { return _id; } }
22         #endregion
23 
24         #region Constructors
25 
26         public ClassList(string id)
27         {
28 
29             this._id = id;
30             _students = new ArrayList() { new Student("12345", "John", "Smith"), new Student("09876", "Jane", "Doe"), new Student("76403", "Bob", "Johnson") };
31 
32         }
33 
34         #endregion
35 
36  
37     }
38 }

 

为了让对象支持foreach 迭代,ClassList类需要实现IEnumerable。因为我们的student是存在ArrayList对象里的,而ArrayList类已经实现了IEnumerable,我们就可以使用ArrayList类的Ienumerable。

1        public IEnumerator GetEnumerator()
2         {
3 
4             return (_students as IEnumerable).GetEnumerator();
5 
6         }

最终的代码贴一下:

 1 using System;
 2 using System.Collections;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6 
 7 namespace IenumerableDemo
 8 {
 9     public class ClassList:IEnumerable
10     {
11 
12         #region private Members
13 
14         private readonly string _id;
15 
16         private ArrayList _students;
17 
18         #endregion
19 
20         #region Properties
21         public string ID { get { return _id; } }
22         #endregion
23 
24         #region Constructors
25 
26         public ClassList(string id)
27         {
28 
29             this._id = id;
30             _students = new ArrayList() { new Student("12345", "John", "Smith"), new Student("09876", "Jane", "Doe"), new Student("76403", "Bob", "Johnson") };
31 
32         }
33 
34         #endregion
35 
36         public IEnumerator GetEnumerator()
37         {
38 
39             return (_students as IEnumerable).GetEnumerator();
40 
41         }
42     }
43 }

然后我们调用看看使用ArrayList的Ienumerable效果:

 1 using System;
 2 using System.Collections;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6 
 7 namespace IenumerableDemo
 8 {
 9     class Program
10     {
11         static void Main(string[] args)
12         {
13             ClassList myClass = new ClassList("History 204");
14 
15 
16             foreach (Student student in myClass)
17 
18                 Console.WriteLine(student);
19 
20 
21             Console.ReadLine();
22         }
23 
24 
25     }
26 
27 
28 }

看来还是实现了效果。那么接下来我们看看自定义实现IEnumerable。实现IEnumerable其实只要实现IEnumerator接口就可以了。

我们创建我们自己的一个自定义类ClassEnumerator 来实现IEnumerator来完成和上面相同的结果。这个类基本上就只是通过_students的索引来进行迭代,Reset()方法就是把索引设置为-1.Current属性来获取当前的student,MoveNext()来跳到Current的下一个数据,并返回一个boolean来表示是否到了集合最后。

 1 using System;
 2 using System.Collections;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6 
 7 namespace IenumerableDemo
 8 {
 9     public class ClassEnumerator : IEnumerator
10     {
11 
12         private ClassList _classList;
13 
14         private int _index;
15 
16         public ClassEnumerator(ClassList classList)
17         {
18             this._classList = classList;
19 
20             _index = -1;
21         }
22 
23         #region IEnumerator Members
24 
25         public void Reset()
26         {
27             this._index = -1;
28         }
29 
30         public object Current
31         {
32             get { return _classList.Students[_index]; }
33         }
34 
35         public bool MoveNext()
36         {
37             _index++;
38             if (_index >= _classList.Students.Count)
39                 return false;
40             else
41                 return true;
42 
43         }
44         #endregion
45 
46     }
47 }

最后修改我们的ClassLst类:

 1 using System;
 2 using System.Collections;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6 
 7 namespace IenumerableDemo
 8 {
 9     public class ClassList : IEnumerable
10     {
11 
12         #region private Members
13 
14         private readonly string _id;
15 
16         private ArrayList _students;
17 
18         #endregion
19 
20         #region Properties
21         public string ID { get { return _id; } }
22 
23         public ArrayList Students { get { return _students; } }
24         #endregion
25 
26         #region Constructors
27 
28         public ClassList(string id)
29         {
30 
31             this._id = id;
32             _students = new ArrayList() { new Student("12345", "John", "Smith"), new Student("09876", "Jane", "Doe"), new Student("76403", "Bob", "Johnson") };
33 
34         }
35 
36         #endregion
37 
38         public IEnumerator GetEnumerator()
39         {
40 
41             return (IEnumerator)new ClassEnumerator(this);
42 
43         }
44     }
45 }

可已看到还是相当简单的。运行结果和上面是一样的。

 下来看看 foreach怎么工作。

其实foreach只是语法糖,最终会被CLR翻译成

1             IEnumerator enumerator = myClass.GetEnumerator();
2             while (enumerator.MoveNext())
3             {
4                 Console.WriteLine((Student)enumerator.Current);
5 
6             }

我们可以把foreach 换成这样试一下,结果是一样滴。

 1 using System;
 2 using System.Collections;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6 
 7 namespace IenumerableDemo
 8 {
 9     class Program
10     {
11         static void Main(string[] args)
12         {
13             ClassList myClass = new ClassList("History 204");
14 
15 
16             //foreach (Student student in myClass)
17 
18             //    Console.WriteLine(student);
19 
20 
21             IEnumerator enumerator = myClass.GetEnumerator();
22             while (enumerator.MoveNext())
23             {
24                 Console.WriteLine((Student)enumerator.Current);
25 
26             }
27 
28             Console.ReadLine();
29         }
30 
31 
32     }
33 
34 
35 }

 

posted @ 2016-03-17 15:13  IT少年  阅读(1088)  评论(0编辑  收藏  举报