1 前言
很多人认为,在C#中 for
和 foreach
功能是一样的,foreach
顶多就是比 for
要更方便一些。但是实际上真的是这样吗?在本文中,让我们通过一个实例来理解其底层的工作原理。
2 for
VS foreach
首先,请看下面的代码段:
1
2
3
4
5
6List<Person> people = new List<Person>(); for(int i = 0; i < 100; i++){ var p = people[i]; // TODO: 下面的代码处理 p }
1
2
3
4
5List<Person> people = new List<Person>(); foreach(var p in people) // TODO: 下面的代码处理p }
我们可以看到,在 TODO 下方,都是直接使用 p 就可以完成相关操作。两者从使用上来说,foreach
能够比 for
省去一个赋值语句(),也就仅此而已,两者感觉完全是一样的,但是实际上真的是这样吗?
3 一个示例
在回答问题之前,让我们再看这样的一个示例。以下代码先定义了一个列表 List<int> list
并添加3亿个整型数,然后分别使用 for
和 foreach
进行累加求和,结果分别保存在 sum1
和 sum2
中,并对这两种方法进行计时,在最后输出计算结果和所用时间。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23static void Main(string[] args) { Console.WriteLine("processing..."); int size = 300_000_000; List<int> list = new List<int>(size); for (int i = 0; i < size; i++) list.Add(i); // 使用 for 求和 var t1 = DateTime.Now; double sum1 = 0; for (int i = 0; i < list.Count; i++) sum1 += list[i]; var t2 = DateTime.Now; // 使用 foreach 求和 double sum2 = 0; foreach (var v in list) sum2 += v; var t3 = DateTime.Now; // 输出结果 Console.WriteLine($"done.nResult: sum1={sum1}, sum2={sum2}"); Console.WriteLine($"time1: {t2 - t1}ntime2: {t3 - t2}"); }
输出如下:
1
2
3
4
5
6processing... done. Result: sum1=44999999767108860, sum2=44999999767108860 time1: 00:00:01.1345446 time2: 00:00:00.6056811
4 原理分析
通过结果分析,我们可以看出,使用 for
循环与 foreach
的用时相差近1倍,通过多次测试也基本是这个结果。这个时间差显然不是误差,根本原因就在于两者实现的方式不同:
for
对 list 中的元素逐一进行访问,由于 list[i] 是列表,所以每次在访问 list[i] 时,需要重新定位,因此要消耗很多的定位时间;foreach
则是只对 list 进行一次遍历,从第1个元素开始直到最后一个元素,具体在实现中使用yield return
来实现(可以参见之前我的文章 深入理解C#中yield return的用法 )。
所以,foreach
是为可迭代的对象(iteratable)专门设计的,能够只遍历一次的情况下,完成对有元素的访问。
明白了这个原理,我们可以将 list 换成数组,再测试一次,代码和结果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24static void Main(string[] args) { //Test1(); //Test2(); Thread.Sleep(4000); Console.WriteLine("processing..."); int size = 300_000_000; int[] arr = new int[size]; for (int i = 0; i < size; i++) arr[i] = i; var t1 = DateTime.Now; double sum1 = 0; for (int i = 0; i < arr.Length; i++) sum1 += arr[i]; var t2 = DateTime.Now; double sum2 = 0; foreach (var v in arr) sum2 += v; var t3 = DateTime.Now; Console.WriteLine($"done.nResult: sum1={sum1}, sum2={sum2}"); Console.WriteLine($"time1: {t2 - t1}ntime2: {t3 - t2}"); }
结果
1
2
3
4
5
6processing... done. Result: sum1=44999999767108860, sum2=44999999767108860 time1: 00:00:00.6329427 time2: 00:00:00.6343659
由于数组有较好的随机访问性能,所以两者的结果基本一样。如果再换一种数据类型,比如 LinkedList,由于其随机读写性能较 List 更差,所以两者的时间差会更大,即 foreach
的优势更明显,有兴趣的读者可以自行尝试一下。
5 总结
C#中的 for
和 foreach
的设计目的是不一样的,for
是一般性的循环,而 foreach
是专门用于可以迭代的集合的循环方法,能够有效地减少访问次数,从而达到优化的效果。因此,在遍历随机访问性能的集合时,两者区别不大,而随机访问性能差时,优先使用foreach
会取得更好的性能。
最后
以上就是腼腆羽毛最近收集整理的关于深度理解 C# 中的 for 和 foreach1 前言2 for VS foreach3 一个示例4 原理分析5 总结的全部内容,更多相关深度理解内容请搜索靠谱客的其他文章。
发表评论 取消回复