ToB企服应用市场:ToB评测及商务社交产业平台

标题: 深度理解 C# 中的 for 和 foreach [打印本页]

作者: 万有斥力    时间: 2022-6-25 12:55
标题: 深度理解 C# 中的 for 和 foreach
1 前言

很多人认为,在C#中 for 和 foreach 功能是一样的,foreach 顶多就是比 for 要更方便一些。但是实际上真的是这样吗?在本文中,让我们通过一个实例来理解其底层的工作原理。
2 for VS foreach

首先,请看下面的代码段:
  1. List<Person> people = new List<Person>();
  2. for(int i = 0; i < 100; i++){
  3.         var p = people[i];
  4.         // TODO: 下面的代码处理 p
  5.        
  6. }
复制代码
  1. List<Person> people = new List<Person>();
  2. foreach(var p in people)
  3.         // TODO: 下面的代码处理p
  4. }
复制代码
我们可以看到,在 TODO 下方,都是直接使用 p 就可以完成相关操作。两者从使用上来说,foreach 能够比 for 省去一个赋值语句(),也就仅此而已,两者感觉完全是一样的,但是实际上真的是这样吗?
3 一个示例

在回答问题之前,让我们再看这样的一个示例。以下代码先定义了一个列表 List list并添加3亿个整型数,然后分别使用 for 和 foreach 进行累加求和,结果分别保存在 sum1 和 sum2 中,并对这两种方法进行计时,在最后输出计算结果和所用时间。
  1. static void Main(string[] args)
  2. {
  3.      Console.WriteLine("processing...");
  4.      int size = 300_000_000;
  5.      List<int> list = new List<int>(size);
  6.      for (int i = 0; i < size; i++)
  7.          list.Add(i);
  8.         // 使用 for 求和
  9.      var t1 = DateTime.Now;
  10.      double sum1 = 0;
  11.      for (int i = 0; i < list.Count; i++)
  12.          sum1 += list[i];
  13.      var t2 = DateTime.Now;
  14.      
  15.      // 使用 foreach 求和
  16.      double sum2 = 0;
  17.      foreach (var v in list)
  18.          sum2 += v;
  19.      var t3 = DateTime.Now;
  20.      
  21.      // 输出结果
  22.      Console.WriteLine($"done.\nResult: sum1={sum1}, sum2={sum2}");
  23.      Console.WriteLine($"time1: {t2 - t1}\ntime2: {t3 - t2}");
  24. }
复制代码
输出如下:
  1. processing...
  2. done.
  3. Result: sum1=44999999767108860, sum2=44999999767108860
  4. time1: 00:00:01.1345446
  5. time2: 00:00:00.6056811
复制代码
4 原理分析

通过结果分析,我们可以看出,使用 for 循环与 foreach 的用时相差近1倍,通过多次测试也基本是这个结果。这个时间差显然不是误差,根本原因就在于两者实现的方式不同:

所以,foreach 是为可迭代的对象(iteratable)专门设计的,能够只遍历一次的情况下,完成对有元素的访问。
明白了这个原理,我们可以将 list 换成数组,再测试一次,代码和结果如下:
  1.         static void Main(string[] args)
  2.         {
  3.             //Test1();
  4.             //Test2();
  5.              Thread.Sleep(4000);
  6.             Console.WriteLine("processing...");
  7.             int size = 300_000_000;
  8.             int[] arr = new int[size];
  9.             for (int i = 0; i < size; i++)
  10.                 arr[i] = i;
  11.             var t1 = DateTime.Now;
  12.             double sum1 = 0;
  13.             for (int i = 0; i < arr.Length; i++)
  14.                 sum1 += arr[i];
  15.             var t2 = DateTime.Now;
  16.             double sum2 = 0;
  17.             foreach (var v in arr)
  18.                 sum2 += v;
  19.             var t3 = DateTime.Now;
  20.             Console.WriteLine($"done.\nResult: sum1={sum1}, sum2={sum2}");
  21.             Console.WriteLine($"time1: {t2 - t1}\ntime2: {t3 - t2}");
  22.         }
复制代码
结果
  1. processing...
  2. done.
  3. Result: sum1=44999999767108860, sum2=44999999767108860
  4. time1: 00:00:00.6329427
  5. time2: 00:00:00.6343659
复制代码
由于数组有较好的随机访问性能,所以两者的结果基本一样。如果再换一种数据类型,比如 LinkedList,由于其随机读写性能较 List 更差,所以两者的时间差会更大,即 foreach 的优势更明显,有兴趣的读者可以自行尝试一下。
5 总结

C#中的 for 和 foreach 的设计目的是不一样的,for 是一般性的循环,而 foreach 是专门用于可以迭代的集合的循环方法,能够有效地减少访问次数,从而达到优化的效果。因此,在遍历随机访问性能的集合时,两者区别不大,而随机访问性能差时,优先使用foreach 会取得更好的性能。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4