迭代器

都知道在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语句使用 本意就是 “迭代出每一个元素”并“生成一个索引器”返回
说到这里相信你们理解到他的精髓了 迭代器 枚举器 索引器 其实他们指的是同一个东西 他们都针对集合使用

好了收工

posted @ 2013-06-23 17:33  assassinx  阅读(670)  评论(0编辑  收藏  举报