.Net学习难点讨论系列11 - foreach与迭代器,枚举数与可枚举值

本文主要整理于ASP.NET2.0开发指南部分内容、C#图解教程

要使一个自定义的集合类可以与foreach一起工作,我们需要实现IEnumerable接口(GetEnumerator方法)或实现IEnumerator<T>接口。

在.NET Framework枚举迭代相关类设计中IEnumerator是基础,其包含三个成员其中一个Current属性,两个函数Movenext与Reset。这些是一个集合对象可枚举的基础。而IEnumerable接口只包含一个GetEnumerator方法,作用就是返回一个IEnumerator对象。

.NET2.0新增的泛型接口IEnumerator<T>实现了IEnumerator与IDisposal接口,该接口自身包含一个Current属性,该属性返回一个T类型的对象。我们实现此接口时,需要实现其自身Current属性及父接口IEnumerator与IDisposal的方法。这其中特别需要注意的是,我们需要显式实现IEnumerator中的Current属性:

1 object System.Collections.IEnumerator.Current

2 {

3     get { throw new NotImplementedException(); }

4 }

另一个泛型接口IEnumerable<T>继承自IEnumerable接口,其自身包含一个GetEnumerator方法返回泛型版本的IEnumerator<T>对象。类似于实现IEnumerator<T>,在实现IEnumerable<T>时,除了实现返回IEnumerator<T>的GetEnumerator方法,还要显示实现IEnumerable中的GetEnumerator方法:

1 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()

2 {

3     throw new NotImplementedException();

4 }

了解了这些后就很容易解释迭代器(yield)了,迭代器提供给我们最简单的创建一个IEnumerator<T>对象或IEnumerable<T>的途径,见下面的代码段:

 1 //返回泛型枚举数

 2 public IEnumerator<string> SampleMethod1()

 3 {

 4     yield return "tree";

 5     yield return "flower";

 6 }

 7 

 8 //返回泛型可枚举对象

 9 public IEnumerable<string> SampleMethod2()

10 {

11     yield return "tree";

12     yield return "flower";

13 }

 

综合以上,下面看一个完整的例子:

 

 1 public class Persons : IEnumerable

 2 {

 3     public string[] m_Names;

 4     public Persons(params string[] Names)

 5     {

 6         m_Names = new string[Names.Length];

 7         Names.CopyTo(m_Names, 0);

 8     }

 9 

10     //实现索引器

11     public string this[int index]

12     {

13         get

14         {

15             return m_Names[index];

16         }

17         set

18         {

19             m_Names[index] = value;

20         }

21     }

22 

23     //实现IEnumerable成员

24     //需返回一个IEnumerator类型的对象

25     #region IEnumerable 成员

26 

27     IEnumerator IEnumerable.GetEnumerator()

28     {

29         return new PersonsEnumerator(this);

30     }

31 

32     #endregion

33 }

34 

35 public class PersonsEnumerator : IEnumerator

36 {

37     int index = -1;

38     Persons P;

39     public PersonsEnumerator(Persons P)

40     {

41         this.P = P;

42     }

43     

44     #region IEnumerator 成员

45 

46     object IEnumerator.Current

47     {

48         get

49         {

50             return P.m_Names[index];

51         }

52     }

53 

54     bool IEnumerator.MoveNext()

55     {

56         int tempIndex = ++index;

57         if (tempIndex >= P.m_Names.Length)

58         {

59             return false;

60         }

61         else

62         {

63             return true;

64         }

65     }

66 

67     void IEnumerator.Reset()

68     {

69         index = -1;

70     }

71     #endregion

72 }

使用(非泛型)迭代器的方式:

1 static void Main()

2 {

3     Persons arrPersons = new Persons("Michel", "Rebecca", "Polaris");

4     foreach (string s in arrPersons)

5     {

6         Console.WriteLine(s);

7     }

8 }

 

在C# 2.0中我们可以通过新增的关键字 – yeild,来方便的实现一个迭代器。而且.NET2.0中新增的泛型可以使迭代器强类型化。

一步步进行:首先我们将上述程序改写为用yield关键字实现(仍然是非泛型)。

 1 public class Persons : IEnumerable

 2 {

 3     public string[] m_Names;

 4     public Persons(params string[] Names)

 5     {

 6         m_Names = new string[Names.Length];

 7         Names.CopyTo(m_Names, 0);

 8     }

 9 

10     #region IEnumerable 成员

11 

12     IEnumerator IEnumerable.GetEnumerator()

13     {

14         for (int i = 0; i < m_Names.Length; i++)

15         {

16             yield return m_Names[i];

17         }

18     }

19 

20     #endregion

21 }

接下来,我们将此方法改写为泛型的实现:

 1 public class Persons<T> : IEnumerable<T>

 2 {

 3     public T[] m_Names;

 4     public Persons(params T[] Names)

 5     {

 6         m_Names = new T[Names.Length];

 7         Names.CopyTo(m_Names, 0);

 8     }

 9 

10     #region IEnumerable<T> 成员

11 

12     IEnumerator<T> IEnumerable<T>.GetEnumerator()

13     {

14         for (int i = 0; i < m_Names.Length; i++)

15         {

16             yield return m_Names[i];

17         }

18     }

19 

20     #endregion

21 }

泛型版迭代器的使用:

1 static void Main()

2 {

3     Persons<string> arrPersons = new Persons<string>("Michel", "Rebecca", "Polaris");

4     foreach (string s in arrPersons)

5     {

6         Console.WriteLine(s);

7     }

8 }

 

上文基本列出了怎样使用yield实现一个迭代器。

下面来说一下yield的一些使用方式:

1. 终止yield迭代的方式

 1 IEnumerator IEnumerable.GetEnumerator()

 2 {

 3     for (int i = 0; i < m_Names.Length; i++)

 4     {

 5         yield return m_Names[i];

 6         if (i >= 1)

 7         {

 8             yield break;

 9         }

10     }

11 }

2. 逆序迭代内容

1 IEnumerator IEnumerable.GetEnumerator()

2 {

3     for (int i = (m_Names.Length - 1); i >= 0; --i)

4     {

5         yield return m_Names[i];

6     }

7 }

3. 迭代器的另一种实现,返回实现IEnumerable<T>接口的对象,不实现GetEnumerator()方法。

 1 public class Persons<T>

 2 {

 3     private T[] m_Names;

 4     public Persons(params T[] Names)

 5     {

 6         m_Names = new T[Names.Length];

 7         Names.CopyTo(m_Names, 0);

 8     }

 9 

10     public IEnumerable<T> GetPersons()

11     {

12         for (int i = 0; i < m_Names.Length; i++)

13         {

14             yield return m_Names[i];

15         }

16     }

17 }

18 

19 class Program

20 {

21     static void Main()

22     {

23         Persons<string> arrPersons = new Persons<string>("Michel", "Rebecca", "Polaris");

24         foreach (string s in arrPersons.GetPersons())

25         {

26             Console.WriteLine(s);

27         }

28     }

29 }

 

你可能感兴趣的:(foreach)