经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » 设计模式 » 查看文章
IL角度理解for 与foreach的区别——迭代器模式
来源:cnblogs  作者:JerryMouseLi  时间:2020/11/16 10:26:44  对本文有异议

IL角度理解for 与foreach的区别——迭代器模式

1 最常用的设计模式

1.1 背景

如果问你最常用的设计模式是哪种?你可能会说单例模式,工厂模式。但根据我在项目里的经验,一个完整的应用,应该是迭代器模式。

1.2 摘要

  • 本文不讲怎么去实现迭代器模式,但介绍迭代器模式到底是什么?为什么迭代器循环迭代时,无法删除元素,无法修改元素;
  • 本文讲的迭代器,媒介主要是C#语言下的foreach,微软爸爸已经在C#的foreach中帮我们实现了迭代器代码,关于迭代器,我们只需要知道他是什么,他的特性是什么,为什么要用他?
  • 迭代器有什么优点,有什么缺点?
  • 在本文中迭代器模式约等于foreach

2 遍历元素

在本项中将会带大家一起进入迭代器模式下的IL代码,看看迭代器内部是怎么去进行循环迭代的;
如下代码实现了for循环打印集合中的所有元素,以及foreach迭代打印集合中的所有元素。

  1. static void Main(string[] args)
  2. {
  3. List<int> intList = new List<int>();
  4. intList.Add(1);
  5. intList.Add(2);
  6. for(var i=0;i<intList.Count;i++)
  7. {
  8. Console.WriteLine("元素for遍历打印:" + intList[i]);
  9. }
  10. foreach (var item in intList)
  11. {
  12. Console.WriteLine("元素foreach遍历打印:" + item);
  13. }
  14. }

代码输出:

可以看到两种打印方式效果一致;for循环任一一种语言都有,大家再熟悉不过,这里不再赘述,我们来看下foreach迭代器模式的IL代码(如果当前网页太小,看不清,请将图片右键另存打开,或者将图片拖到浏览器新窗口打开);

大概思路如下:List 下有个迭代器Enumerator,可以通过List下的GetEnumerator方法获得此迭代器集合;然后通过迭代器的get_Current方法来获取当前第一个元素。迭代器的MoveNext()方法判断下一元素是否存在,如果存在取出循环;不存在结束迭代循环模式;最终,将迭代过程中产生的系统非托管资源释放;

迭代器代码如下:

  1. var enumerator = intList.GetEnumerator();
  2. while (enumerator.MoveNext())
  3. {
  4. Console.WriteLine("迭代器模式遍历打印:" + enumerator.Current);
  5. }
  6. Console.ReadLine();

3 删除元素

3.1 for删除例子

  1. List<int> intList = new List<int>();
  2. intList.Add(1);
  3. intList.Add(2);
  4. for(var i=0;i<intList.Count;i++)
  5. {
  6. if (intList[i] == 1)
  7. { intList.Remove(1); }
  8. }
  9. Console.WriteLine("集合中元素数量:" + intList.Count);

Console输出
集合中元素数量:1
这说明for删除成功

3.2 foreach删除例子

  1. foreach (var item in intList)
  2. {
  3. if (item==1)
  4. { intList.Remove(1); }
  5. }
  6. Console.WriteLine("集合中元素数量:" + intList.Count);

从如上抛出的异常得得知 var enumerator = intList.GetEnumerator();迭代器在循环迭代中是不允许对该迭代器进行删除操作的。如果需要删除操作,可以再这个集合基础上做linq操作;因为迭代器在循环迭代中是一个值对象的存在,不允许变更迭代器对象的内容;

思考

如下代码也是运用了迭代器模式,为什么能删除集合元素了?读者自行思考哈。这也是迭代器方式下牺牲内存来删除/修改元素的一种方法。

  1. foreach (var item in dictionary.ToList()) {
  2. Debug.Log(item.Key + "," + item.Value);
  3. if (item.Key.Equals("s11")) {
  4. dictionary.Remove(item.Key);
  5. }
  6. }

4 修改元素

4.1 for修改

  1. List<int> intList = new List<int>();
  2. intList.Add(1);
  3. intList.Add(2);
  4. for(var i=0;i<intList.Count;i++)
  5. {
  6. if (intList[i] == 1)
  7. { intList[i]=3; }
  8. Console.WriteLine("集合中元素遍历:" + intList[i]);
  9. }

输出:

  1. 集合中元素遍历:3
  2. 集合中元素遍历:2

从如上输出看出,for修改成功

4.2 foreach修改

直接编译就通不过

5 修改元素属性

如下程序,会去修改迭代器元素的属性,并且在for跟foreach下都能运行成功。

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. List<protocol> protocols = new List<protocol>();
  6. var p1 = new protocol();
  7. p1.bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
  8. var p2 = new protocol();
  9. p2.bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
  10. protocols.Add(p1);
  11. protocols.Add(p2);
  12. foreach (var item in protocols)
  13. {
  14. item.bytes = p2.bytes;
  15. item.intList.Add(1);
  16. }
  17. for (var i=0;i<protocols.Count;i++)
  18. {
  19. protocols[i].bytes = p1.bytes;
  20. protocols[i].intList.Add(1);
  21. protocols[i] = p1;
  22. }
  23. Console.WriteLine("程序运行成功");
  24. Console.ReadLine();
  25. }
  26. }
  27. public class protocol
  28. {
  29. public byte[] bytes= { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
  30. public List<int> intList=new List<int>();
  31. }

6 什么场景下用foreach

既然,for效率更高,foreach效率不及for。for能做的事foreach不能做(修改删除元素)。是不是没有foreach的用武之地了呢,大错特错了,恰恰相反,每个项目里用的最多的反而是foreach迭代器模式,我统计了下超过工厂模式跟单例模式,不信你自己去数foreach的数量。但是在遍历散列集合时,如字典,for就会失效了,因为下标不是有序数值了,而是看上去无序的内部散列算法。这时foreach遍历字典就很有用,他定义了一个枚举器(迭代器),而这个枚举器统一了,getCurrent(), moveNext()等用于迭代的方法;

  1. foreach (var item in dictionary.ToList()) {
  2. Debug.Log(item.Key + "," + item.Value);
  3. if (item.Key.Equals("s11")) {
  4. dictionary.Remove(item.Key);
  5. }
  6. }

像上面的用for,for循环的i与字典的key(自定义,一般是string类型)是没办法建立映射关系的;所以无法用for循环

7 小结

  1. for与foreach都可以遍历数组/集合,不过for则在较复杂的循环中效率更高;
  2. foreach不可以删除/修改集合元素,而for可以;
  3. foreach和for都可以修改元素里面的属性;
  4. foreach适合遍历用了哈希算法的散列,集合,字典;

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://www.cnblogs.com/JerryMouseLi/p/13977500.html

原文链接:http://www.cnblogs.com/JerryMouseLi/p/13977500.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号