zoukankan      html  css  js  c++  java
  • 迭代器

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

    好了收工

  • 相关阅读:
    走进__proto__属性,看ie是否支持它,谁又来给他归宿
    如何安装zip格式的MySQL
    博客初心源于前端攻城狮
    Day 1: How to install jedi/codeintel plugin for sublime on Linux
    关于拓扑排序的一些想法
    POJ1061 青蛙的约会 __一维世界的爱情
    缩步查找法——一种新的查找算法
    HDU3371 Connect the Cities
    HDU1598 find the most comfortable road
    codeforces 349 div2.c
  • 原文地址:https://www.cnblogs.com/assassinx/p/3151314.html
Copyright © 2011-2022 走看看