平时工作中我们经常用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如下:

 



在自己的对象里实现IEnumerator和IEnumerable_源代码

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


在自己的对象里实现IEnumerator和IEnumerable_数据_02


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

 



在自己的对象里实现IEnumerator和IEnumerable_构造函数_03

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 }


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


 

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



在自己的对象里实现IEnumerator和IEnumerable_迭代_05

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


在自己的对象里实现IEnumerator和IEnumerable_构造函数_06


最终的代码贴一下:



在自己的对象里实现IEnumerator和IEnumerable_源代码_07

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 }


在自己的对象里实现IEnumerator和IEnumerable_数据_08


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



在自己的对象里实现IEnumerator和IEnumerable_数据_09

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 }


在自己的对象里实现IEnumerator和IEnumerable_迭代_10


在自己的对象里实现IEnumerator和IEnumerable_构造函数_11

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

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



在自己的对象里实现IEnumerator和IEnumerable_数据_12

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 }


在自己的对象里实现IEnumerator和IEnumerable_构造函数_13


最后修改我们的ClassLst类:



在自己的对象里实现IEnumerator和IEnumerable_数据_14

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 }


在自己的对象里实现IEnumerator和IEnumerable_迭代_15


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

 下来看看 foreach怎么工作。

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



在自己的对象里实现IEnumerator和IEnumerable_迭代_16

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


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


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



在自己的对象里实现IEnumerator和IEnumerable_数据_18

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 }


在自己的对象里实现IEnumerator和IEnumerable_构造函数_19


 


编程是个人爱好



平时工作中我们经常用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如下:

 



在自己的对象里实现IEnumerator和IEnumerable_迭代_20

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


在自己的对象里实现IEnumerator和IEnumerable_数据_21


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

 



在自己的对象里实现IEnumerator和IEnumerable_构造函数_22

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 }


在自己的对象里实现IEnumerator和IEnumerable_构造函数_23


 

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



在自己的对象里实现IEnumerator和IEnumerable_数据_24

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


在自己的对象里实现IEnumerator和IEnumerable_源代码_25


最终的代码贴一下:



在自己的对象里实现IEnumerator和IEnumerable_迭代_26

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 }


在自己的对象里实现IEnumerator和IEnumerable_数据_27


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



在自己的对象里实现IEnumerator和IEnumerable_迭代_28

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 }


在自己的对象里实现IEnumerator和IEnumerable_迭代_29


在自己的对象里实现IEnumerator和IEnumerable_构造函数_11

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

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



在自己的对象里实现IEnumerator和IEnumerable_构造函数_31

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 }


在自己的对象里实现IEnumerator和IEnumerable_构造函数_32


最后修改我们的ClassLst类:



在自己的对象里实现IEnumerator和IEnumerable_源代码_33

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 }


在自己的对象里实现IEnumerator和IEnumerable_构造函数_34


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

 下来看看 foreach怎么工作。

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



在自己的对象里实现IEnumerator和IEnumerable_迭代_35

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


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


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



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

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 }


在自己的对象里实现IEnumerator和IEnumerable_迭代_38