迭代器

都知道在c#2.0里有 迭代器,那么这个迭代器到底是个啥东东呢 他跟foreach又有啥关系

先来说下什么是迭代器

1 IList<string> arr = new List<string>();

2 arr.Add("smith");

3 arr.Add("sherry");

4 arr.Add("steve");

5 arr.Add("salt");

6 arr.Add("stefan");

7 IEnumerator<string> emutor = arr.GetEnumerator();

8 while (emutor.MoveNext())

9     Console.WriteLine(emutor.Current);

这就是迭代器 他是专门针对集合元素的 ilist<string> 就是一个集合元素
ienumerator<string> 就是他的迭代器。可以通过moveNext()来枚举出他的所有子元素。

呐 我们来把while子句那一段换成这两句

1 foreach (string item in arr)

2     Console.WriteLine(item);

用ildasm进行反编译可以看到他们的il代码是完全一样的

 1 IL_0045:  callvirt   instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<string>::GetEnumerator()

 2   IL_004a:  stloc.2

 3   .try

 4   {

 5     IL_004b:  br.s       IL_005b

 6     IL_004d:  ldloc.2

 7     IL_004e:  callvirt   instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<string>::get_Current()

 8     IL_0053:  stloc.1

 9     IL_0054:  ldloc.1

10     IL_0055:  call       void [mscorlib]System.Console::WriteLine(string)

11     IL_005a:  nop

12     IL_005b:  ldloc.2

13     IL_005c:  callvirt   instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()

14     IL_0061:  stloc.3

15     IL_0062:  ldloc.3

16     IL_0063:  brtrue.s   IL_004d

17     IL_0065:  leave.s    IL_0077

18   }  // end .try

19   finally

20   {

21     IL_0067:  ldloc.2

22     IL_0068:  ldnull

23     IL_0069:  ceq

24     IL_006b:  stloc.3

25     IL_006c:  ldloc.3

26     IL_006d:  brtrue.s   IL_0076

27     IL_006f:  ldloc.2

28     IL_0070:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()

29     IL_0075:  nop

30     IL_0076:  endfinally

31   }  // end handler
View Code

跟foreach的关系

foreach这个是原来就有的语法 就是让你的c#代码“更优雅”由编译器支持的 让你代替 for(int i,i<5,i++)这样的写法
诸如此类的例子在c#里还有很多
呐看下这段代码呢

1 string[] arr = new string[] { "smith", "sherry", "Steve", "salt", "stefan" };

2 foreach (string item in arr)

3     Console.WriteLine(item);

注意啊 虽然都是foreach 但是此arr非彼arr 不存在迭代器的说法 ,这个是数组
通过ildasm反编译你就可以看到并没有类似::GetEnumerator()这样的代码存在

但是这种代码又不一样,因为显示使用了迭代器

1 string[] arr = new string[] { "smith", "sherry", "Steve", "salt", "stefan" };

2 IEnumerator emutor = arr.GetEnumerator();

3 while (emutor.MoveNext())

4     Console.WriteLine(emutor.Current);

使用il反编译的代码:

1 IL_0033:  callvirt   instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Array::GetEnumerator()

2   IL_0038:  stloc.1

3   IL_0039:  br.s       IL_0047

4   IL_003b:  ldloc.1

5   IL_003c:  callvirt   instance object [mscorlib]System.Collections.IEnumerator::get_Current()

6   IL_0041:  call       void [mscorlib]System.Console::WriteLine(object)

7   IL_0046:  nop

8   IL_0047:  ldloc.1

9   IL_0048:  callvirt   instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
View Code

怎样让你自己设计的集合类也应用迭代器呢

那么首先你得设计一个集合类 这个跟索引属性有点类似

 1 public class Student

 2 {

 3     public string name { get; set; }

 4     public int score { get; set; }

 5     public Student(string firstName, int lastName)

 6     {

 7         this.name = firstName;

 8         this.score = lastName;

 9     }

10 }

11 //实现ienumerable接口的可枚举类型

12 

13 public class Students : IEnumerable

14 {

15     private Student[] arr;

16     public Students(Student[] peoples)

17     {

18         arr = new Student[peoples.Length];

19         for (int i = 0; i < peoples.Length; i++)

20         {

21             arr[i] = peoples[i];

22         }

23     }

24     public IEnumerator GetEnumerator()

25     {

26         return new StudentsEnum(arr);

27     }

28 }

然后你得实现他的枚举器

 1 //实现ienumerator接口的枚举函数 

 2 public class StudentsEnum : IEnumerator

 3 {

 4     private Student[] arr;

 5     //当前项的索引

 6     //这里是指针放到第一个集合成员之前

 7     //而非指向第一个集合成员

 8     private int i = -1;

 9     public StudentsEnum(Student[] peoples)

10     {

11         this.arr = peoples;

12     }

13     #region IEnumerator 成员

14     public object Current

15     {

16         get

17         {

18             try

19             {

20                 return arr[i];

21             }

22             catch (IndexOutOfRangeException)

23             {

24                 throw new IndexOutOfRangeException();

25             }

26         }

27     }

28     public bool MoveNext()

29     {

30         i++;

31         return i < arr.Length;

32     }

33 

34     public void Reset()

35     {

36         i = -1;

37     }

38     #endregion

39 }

实现了students集合的迭代器 那么我们接下来就可以使用它了

1 Student[] peopleArray = new Student[3] { new Student("smith", 45), new Student("sherry", 56), new Student("salt", 67) };

2 Students people = new Students(peopleArray);

3 foreach (Student p in people)

4     Console.WriteLine("name:{0} \t score:{1}", p.name, p.score);

综上所述 总之注意3个要素

  1. 确定子元素
  2. 为子元素设计集合类 并继承IEnumerable
    主要是增加这个方法就可以了
    public IEnumerator GetEnumerator()
        {
            return new StudentsEnum(arr);
        }
  3. 实现集合类的枚举器
  4. 使用集合时 调用GetEnumerator 获取枚举器 进行枚举,或者直接使用foreach子句

其实主要代码也就是枚举器的实现
我们有种偷懒的做法 ,那就是使用yield关键字

 1 public class City

 2 {

 3     #region IEnumerable 成员

 4     string[] m_Cities = { "New York", "Paris", "London" };

 5     public IEnumerator GetEnumerator()

 6     {

 7         Console.WriteLine(@"开始迭代~\(≧▽≦)/~啦啦啦");

 8         for (int i = 0; i < m_Cities.Length; i++)

 9             yield return m_Cities[i]; // 产生枚举元素

10     }

11     #endregion

12 }

还是一样的调用

1 City c = new City();

2 foreach (string item in c)

3     Console.WriteLine(item);

这都啥啊 我看着都晕了student类也没了 city的子元素是啥啊, 这不是索引器吗 像这样:

1 public string this[int n]

2 {

3     get { return m_Cities[n]; }

4     set { m_Cities[n] = value; }

5 }

你看都没有继承IEnumerable 还是可以同样的使用
是的GetEnumerator() 本意就是“获取一个索引器
yield意思是 迭代 量产
而yield 搭配for语句使用 本意就是 “迭代出每一个元素”并“生成一个索引器”返回
说到这里相信你们理解到他的精髓了 迭代器 枚举器 索引器 其实他们指的是同一个东西 他们都针对集合使用

好了收工

你可能感兴趣的:(迭代器)