一、前言

假设存在一个数组,其遍历模式是根据索引进行遍历的;又假设存在一个HashTable,其遍历模式是根据键值进行遍历的;无论哪种集合,如果它们的遍历没有一个共同的接口,那么在客户端进行调用的时候,就需要对每种集合的具体类型进行它们各自的具体代码编写,当需求发生变化时,就必须修改我们的代码。并且客户端过多的关注集合内部的实现,代码的移植性就会变差,违反了开闭原则,这个时候迭代器就诞生了,现在我们来根据上一章 foreach遍历原理(一)实现我们自己的迭代器。

二、代码示例

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. //使用接口IMyEnumerable代替MyList
  6. IMyEnumerable list = new MyList();
  7. //得到迭代器,在循环中针对迭代器编码,而不是集合MyList
  8. IMyEnumerator enumerator = list.GetEnumerator();
  9.  
  10. while (enumerator.MoveNext())
  11. {
  12. object current = enumerator.Current;
  13. Console.WriteLine(current);
  14. }
  15. Console.ReadKey();
  16. }
  17.  
  18. /// <summary>
  19. /// 要求所有的迭代器全部实现该接口
  20. /// </summary>
  21. interface IMyEnumerator
  22. {
  23. bool MoveNext();
  24. object Current { get; }
  25. }
  26.  
  27. /// <summary>
  28. /// 要求所有的集合实现该接口
  29. /// 这样一来,客户端就可以针对该接口编码,
  30. /// 而无须关注具体的实现
  31. /// </summary>
  32. interface IMyEnumerable
  33. {
  34. IMyEnumerator GetEnumerator();
  35. int Count { get; }
  36. }
  37.  
  38. class MyList : IMyEnumerable
  39. {
  40. ]{,,,,,,,,,};
  41. IMyEnumerator myEnumerator;
  42.  
  43. public object this[int i]
  44. {
  45. get { return items[i]; }
  46. set { this.items[i] = value; }
  47. }
  48.  
  49. public int Count
  50. {
  51. get { return items.Length; }
  52. }
  53.  
  54. public IMyEnumerator GetEnumerator()
  55. {
  56. if (myEnumerator == null)
  57. {
  58. myEnumerator = new MyEnumerator(this);
  59. }
  60. return myEnumerator;
  61. }
  62. }
  63.  
  64. class MyEnumerator : IMyEnumerator
  65. {
  66. ;
  67. MyList myList;
  68. public MyEnumerator(MyList myList)
  69. {
  70. this.myList = myList;
  71. }
  72.  
  73. public bool MoveNext()
  74. {
  75. > myList.Count)
  76. {
  77. index = ;
  78. return false;
  79. }
  80. else
  81. {
  82. index++;
  83. return true;
  84. }
  85. }
  86.  
  87. public object Current
  88. {
  89. ]; }
  90. }
  91. }
  92.  
  93. }

运行结果:

三、疑问——为什么不把 IMyEnumerable 和 IMyEnumerator 接口写在同一个接口里面,例如新建一个接口名字为 IForeach,不使用迭代器,也能输出上面的结果

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. IForeach iForeach = new MyList();
  6. while (iForeach.MoveNext())
  7. {
  8. object current = iForeach.Current;
  9. Console.WriteLine(current);
  10. }
  11. Console.ReadKey();
  12. }
  13.  
  14. interface IForeach
  15. {
  16. bool MoveNext();
  17. object Current { get; }
  18. int Count { get; }
  19. }
  20.  
  21. class MyList : IForeach
  22. {
  23. ] { , , , , , , , , , };
  24. ;
  25.  
  26. public object this[int i]
  27. {
  28. get { return items[i]; }
  29. set { this.items[i] = value; }
  30. }
  31.  
  32. public int Count
  33. {
  34. get { return items.Length; }
  35. }
  36.  
  37. public bool MoveNext()
  38. {
  39. > Count)
  40. {
  41. index = ;
  42. return false;
  43. }
  44. else
  45. {
  46. index++;
  47. return true;
  48. }
  49. }
  50.  
  51. public object Current
  52. {
  53. ]; }
  54. }
  55. }
  56. }

四、如果我现在有新需求要倒序输出,那么按照上面“三”的做法,就必须修改 MyList  类里面的代码,而且是大改。如果使用迭代器 ,我们只需要重新写类继承迭代器IMyEnumerator,替换一下迭代器,就能够实现MyList  的倒序输出。

新增一个倒序迭代器

  1. class MyInvertEnumerator : IMyEnumerator
  2. {
  3. ;
  4. MyList myList;
  5. public MyInvertEnumerator(MyList myList)
  6. {
  7. this.myList = myList;
  8. index = myList.Count;
  9. }
  10.  
  11. public bool MoveNext()
  12. {
  13. <)
  14. {
  15. index = myList.Count;
  16. return false;
  17. }
  18. else
  19. {
  20. index--;
  21. return true;
  22. }
  23. }
  24.  
  25. public object Current
  26. {
  27. get { return myList[index]; }
  28. }
  29. }

修改MyList集合里面获取迭代器的方法,然后运行就能够

  1. class MyList : IMyEnumerable
  2. {
  3. ]{,,,,,,,,,};
  4. IMyEnumerator myEnumerator;
  5.  
  6. public object this[int i]
  7. {
  8. get { return items[i]; }
  9. set { this.items[i] = value; }
  10. }
  11.  
  12. public int Count
  13. {
  14. get { return items.Length; }
  15. }
  16.  
  17. public IMyEnumerator GetEnumerator()
  18. {
  19. if (myEnumerator == null)
  20. {
  21. // myEnumerator = new MyEnumerator(this);//正序输出
  22. myEnumerator = new MyInvertEnumerator(this);//倒序输出
  23. }
  24. return myEnumerator;
  25. }
  26. }

倒序输出完整代码:

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. //使用接口IMyEnumerable代替MyList
  6. IMyEnumerable list = new MyList();
  7. //得到迭代器,在循环中针对迭代器编码,而不是集合MyList
  8. IMyEnumerator enumerator = list.GetEnumerator();
  9.  
  10. while (enumerator.MoveNext())
  11. {
  12. object current = enumerator.Current;
  13. Console.WriteLine(current);
  14. }
  15. Console.ReadKey();
  16. }
  17.  
  18. /// <summary>
  19. /// 要求所有的迭代器全部实现该接口
  20. /// </summary>
  21. interface IMyEnumerator
  22. {
  23. bool MoveNext();
  24. object Current { get; }
  25. }
  26.  
  27. /// <summary>
  28. /// 要求所有的集合实现该接口
  29. /// 这样一来,客户端就可以针对该接口编码,
  30. /// 而无须关注具体的实现
  31. /// </summary>
  32. interface IMyEnumerable
  33. {
  34. IMyEnumerator GetEnumerator();
  35. int Count { get; }
  36. }
  37.  
  38. class MyList : IMyEnumerable
  39. {
  40. ]{,,,,,,,,,};
  41. IMyEnumerator myEnumerator;
  42.  
  43. public object this[int i]
  44. {
  45. get { return items[i]; }
  46. set { this.items[i] = value; }
  47. }
  48.  
  49. public int Count
  50. {
  51. get { return items.Length; }
  52. }
  53.  
  54. public IMyEnumerator GetEnumerator()
  55. {
  56. if (myEnumerator == null)
  57. {
  58. // myEnumerator = new MyEnumerator(this);//正序输出
  59. myEnumerator = new MyInvertEnumerator(this);//倒序输出
  60. }
  61. return myEnumerator;
  62. }
  63. }
  64.  
  65. class MyEnumerator : IMyEnumerator
  66. {
  67. ;
  68. MyList myList;
  69. public MyEnumerator(MyList myList)
  70. {
  71. this.myList = myList;
  72. }
  73.  
  74. public bool MoveNext()
  75. {
  76. > myList.Count)
  77. {
  78. index = ;
  79. return false;
  80. }
  81. else
  82. {
  83. index++;
  84. return true;
  85. }
  86. }
  87.  
  88. public object Current
  89. {
  90. ]; }
  91. }
  92. }
  93.  
  94. class MyInvertEnumerator : IMyEnumerator
  95. {
  96. ;
  97. MyList myList;
  98. public MyInvertEnumerator(MyList myList)
  99. {
  100. this.myList = myList;
  101. index = myList.Count;
  102. }
  103.  
  104. public bool MoveNext()
  105. {
  106. <)
  107. {
  108. index = myList.Count;
  109. return false;
  110. }
  111. else
  112. {
  113. index--;
  114. return true;
  115. }
  116. }
  117.  
  118. public object Current
  119. {
  120. get { return myList[index]; }
  121. }
  122. }
  123.  
  124. }

迭代器就讲到这里了,谢谢大家。

foreach遍历扩展(二)的更多相关文章

  1. c#--foreach遍历的用法与split的用法

    一. foreach循环用于列举出集合中所有的元素,foreach语句中的表达式由关键字in隔开的两个项组成.in右边的项是集合名,in左边的项是变量名,用来存放该集合中的每个元素.      该循环 ...

  2. 用数组指针遍历数组,FOR/FOREACH遍历数组

    1. 用数组指针遍历一维数组 <?php header("Content-type:text/html;charset=utf-8"); /*用数组指针遍历一位数组的值*/ ...

  3. foreach遍历数组

    foreach遍历一维数组 <?php //PHP数组遍历:foreach //定义数组 $arr=array(1,2,3,4,5,6,7,8,9,10); //foreach循环 foreac ...

  4. C# 表达式树遍历(二)

    一.前言 上一篇我们对表达式树有了初步的认识,这里我们将对表达式树进行遍历,只有弄清楚了他的运行原理,我们才可以对他进行定制化修改. 表达式系列目录 C# 表达式树讲解(一) C# 表达式树遍历(二) ...

  5. C#实现在foreach遍历中删除集合中的元素(方法总结)

    目录 方法一:采用for循环,并且从尾到头遍历 方法二:使用递归 方法三:通过泛型类实现IEnumerator 在foreach中删除元素时,每一次删除都会导致集合的大小和元素索引值发生变化,从而导致 ...

  6. 关于for与forEach遍历集合中对集合进行操作的问题

    遍历List集合,在循环中再对List集合进行操作,有时候会遇到ConcurrentModificationException(并发修改异常);其实只有在forEach循环集合再对集合操作会发生异常: ...

  7. 用<forEach>遍历list集合时,提示我找不到对象的属性

    <c:forEach items="${list}" var="item"> <tr> <td>${item.UserId} ...

  8. Foreach遍历

    前天在项目中遇到一个问题,foreach遍历过程中修改responses中的对象,其中responses的类型:IEnumerable<Order>,代码如下: foreach (Orde ...

  9. 使用yield关键字让自定义集合实现foreach遍历

    一般来说当我们创建自定义集合的时候为了让其能支持foreach遍历,就只能让其实现IEnumerable接口(可能还要实现IEnumerator接口) 但是我们也可以通过使用yield关键字构建的迭代 ...

随机推荐

  1. javascript 不用ajax 用 iframe 子域名下做到ajax post数据

    最近在一个项目中遇到了ajax跨域的问题,情况如下.有三个域名分别是 a.xx.com b.xx.com c.xx.com 这三个域名都会用用ajax post方式相互读取数据.文笔不好, 不写了妈蛋 ...

  2. Python自动化运维之8、正则表达式re模块

    re模块 正则表达式使用单个字符串来描述.匹配一系列符合某个句法规则的字符串,在文本处理方面功能非常强大,也经常用作爬虫,来爬取特定内容,Python本身不支持正则,但是通过导入re模块,Python ...

  3. 04 - 替换vtkDataObject中的GetPipelineInformation 和GetExecutive 方法 VTK 6.0 迁移

    VTK6 引入了许多不兼容的变.其中之一是删除vtkDataObject中所有有关管道的方法.其中的两个方法就是GetPipelineInformation() 和 GetExecutive().这些 ...

  4. 关于nginx架构探究(4)

    事件管理机制 Nginx是以事件驱动的,也就是说Nginx内部流程的向前推进基本都是靠各种事件的触发来驱动,否则Nginx将一直阻塞在函数epoll_wait()或suspend函数,Nginx事件一 ...

  5. 转:exit()与_exit()的区别

    版权声明:本文为博主原创文章,未经博主允许不得转载. 从图中可以看出,_exit 函数的作用是:直接使进程停止运行,清除其使用的内存空间,并清除其在内核的各种数据结构:exit 函数则在这些基础上做了 ...

  6. 用MySQL创建数据库和数据库表

    1.使用SHOW语句找出在服务器上当前存在什么数据库: mysql> SHOW DATABASES; +----------+ | Database | +----------+ | mysql ...

  7. 【网贷投资手册】P2P行业揭秘

    [网贷投资手册]P2P行业揭秘     (中国电子商务研究中心讯)如果你手头有100元,你会拿它来做什么?跟好朋友去吃一顿?跟女朋友去看场电影?还是……你会想到拿100元去投资吗?100元太少了,买一 ...

  8. DELPHI7如何调用带参数的JAVA WebService

    对方提供的WebService地址是http://192.168.1.6:8080/test/pic?XH=XX用DELPHI如何调呢 ------解决方案--------------------通过 ...

  9. Android取得电池的电量

    首先需要用到的是一个类继承BrocastReceiver 2 代码如下: public class Battery_Info extends BroadcastReceiver { @Override ...

  10. 在 SQL Server 2012 附加 Adventure Works 範例資料庫

    原文地址:http://technet.microsoft.com/zh-tw/library/eb1f9417-4cca-4575-a725-187bcd60c7e7 附加数据库时报错 错误5123 ...