c#中容易被忽视的foreach
有句俗语:百姓日用而不知。我们c#程序员很喜欢,也非常习惯地用foreach。今天呢,我就带大家一起探索foreach,走,开始我们的旅程。
一、for语句用的好好的,为什么要提供一个foreach?
for (var i = 0; i < 10; i++)
{
//to do sth
} foreach (var n in list)
{
//to do sth
}
首先,for循环,需要知道循环的次数,foreach不需要。其次,for循环在遍历对象的时候,略显麻烦,还需要通过下标索引找到当前对象,foreach不需要这么麻烦,显得更优雅。最后,for循环需要知道集合的细节,foreach不需要知道。
这一切的好处,得益于微软的封装,那我们看看foreach生成的IL代码:
IL_00a7: callvirt instance valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<!0>
class [System.Collections]System.Collections.Generic.List`1<int64>::GetEnumerator()
.try
{
IL_00ae: br.s IL_00c9
IL_00b0: ldloca.s V_10
IL_00b2: call instance !0 valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<int64>::get_Current() IL_00cb: call instance bool valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<int64>::MoveNext()
IL_00d0: brtrue.s IL_00b0
IL_00d2: leave.s IL_00e3
} // end .try
finally
{
IL_00d6: constrained. valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<int64>
IL_00dc: callvirt instance void [System.Runtime]System.IDisposable::Dispose()
IL_00e1: nop
IL_00e2: endfinally
} // end handlers
怎样的对象才能使用foreach呢?从微软的文档上看,实现了IEnumerable接口的对象,可以使用foreach,此接口只定义了一个方法:public System.Collections.IEnumerator GetEnumerator (); 有意思的是,它返回了一个IEnumerator接口,再看看这个接口:
有一个属性:Current和两个方法MoveNext()、Reset(),现在我们回过头来看看生成的IL代码,真相大白。foreach只不过是个好吃的语法糖而已,编译器帮我们做好了一切。和直接写foreach类似的用法还有一个,就是对象的Foreach方法:
list.ForEach(n =>
{
//to do sth
});
那问题就来了,都是foreach,我该用哪个?忍不住看看微软的源码:
internal void ForEach(Action<T> action)
{
foreach (T x in this)
{
action(x);
}
}
其实,就是定义了一个委托,我们把想要做的事情定义好,它来执行。这和直接使用foreach有何区别?我又忍不住好奇心,写了一段代码,比较了for和foreach的性能,先上结果:

说明下,最后一个是对象调用Foreach方法。数据反映的是随着数据规模下降,看运行时间有什么变化。从1亿次循环到1万次循环,耗时从几百毫秒到1毫秒以内。从图上,明显能看出性能差异,是从千万级别开始,for的性能最好,其次是对象的Foreach方法,最后是foreach。
for和foreach的性能差异,我们尚且能理解,但是对象的Foreach和直接foreach差异从何而来?我冥思苦想,百思不得其解。我试图从内存分配和垃圾回收的机制方向去理解,但是没有突破。我想着,直接foreach耗时,是不是因为,它多执行了什么东西,比如说多分配了一些变量,比如说,内存中这么大数据量,垃圾回收机制,不可能无动于衷,是不是垃圾回收机制导致的程序变慢,进而影响了性能。

我在循环完后,强行执行了一次GC,才释放了13.671875k,说明循环中,执行GC也没有什么意义,回收不了垃圾,但是如果循环中,频繁执行GC,确实会导致程序没法好好地运行。垃圾回收机制,会把不再引用的对象释放,而整个循环过程中,对象都在List中,所以GC应该不会运行。
那亲爱的程序员朋友,你觉得对象的Foreach方法和直接Foreach的性能差异,是怎么产生的呢,欢迎讨论,我把源码贴出来。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text; namespace MyConsole.Test
{
public class ForeachTest
{
public static void Test(long num)
{
Console.WriteLine("当前数据规模:" + num); DateTime start = DateTime.Now; for (var i = 0; i < num; i++)
{
var t = (i + 1) * 100 + 1;
} DateTime end = DateTime.Now; var costTime = end.Subtract(start).TotalMilliseconds; Console.WriteLine("for cost time:" + costTime + " ms"); List<long> list = new List<long>();
for (var i = 0; i < num; i++)
{
list.Add(i);
} start = DateTime.Now; foreach (var n in list)
{
var t = (n + 1) * 100 + 1;
} end = DateTime.Now; costTime = end.Subtract(start).TotalMilliseconds; Console.WriteLine("foreach cost time:" + costTime + " ms"); start = DateTime.Now; list.ForEach(n =>
{
var t = (n + 1) * 100 + 1;
}); end = DateTime.Now; costTime = end.Subtract(start).TotalMilliseconds; Console.WriteLine("obj foreach cost time:" + costTime + " ms"); Console.WriteLine("--------------------------------------------");
Console.WriteLine("");
}
}
}
放到Main方法里:
long[] nums =
{
100000000,
10000000,
1000000,
100000,
10000,
}; foreach (int num in nums)
{
for (int i = 0; i < 5; i++)
{
ForeachTest.Test(num);
}
} Console.ReadLine();
最后注意一点的是,foreach循环里面,不能随便添加或者删除元素,如果允许的话,程序将很难控制,而且非常容易出错,所以微软不允许这么干。
c#中容易被忽视的foreach的更多相关文章
- 了解PHP中的Array数组和foreach
1. 了解数组 PHP 中的数组实际上是一个有序映射.映射是一种把 values 关联到 keys 的类型.详细的解释可参见:PHP.net中的Array数组 . 2.例子:一般的数组 这里,我 ...
- Java中LinkedList的fori和foreach效率比较
在<Java中ArrayList的fori和foreach效率比较>中对ArrayList的两种循环方式进行了比较,本次对LinkedList的两种遍历方式进行效率的比较. 1. list ...
- Mybatis中的in查询和foreach标签
Mybatis中的foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合. foreach元素的属性主要有 item,index,collection,open,separato ...
- Java中的可变参数以及foreach语句
Java中的可变参数的定义格式如下: 返回值类型 方法名称(类型 ... 参数名称){} foreach语句的格式如下: for ( 数据类型 变量名称 :数据名称){ ... } public ...
- Java中的增强 for 循环 foreach
foreach 是 Java 中的一种语法糖,几乎每一种语言都有一些这样的语法糖来方便程序员进行开发,编译期间以特定的字节码或特定的方式来对这些语法进行处理.能够提高性能,并减少代码出错的几率.在 J ...
- 空数组在以下三种遍历中均不可更改:forEach、map和for...in
首先,我们要知道对于forEach.map和for...in三种遍历,在不是空数组的情况下,要想实现更改原数组的方法,代码如下: var list = [1,2,3,4]; var list1 = [ ...
- js数组中的find(), findIndex(), filter(), forEach(), some(), every(), map(), reduce()方法的详解和应用实例
1. find()与findIndex() find()方法,用于找出第一个符合条件的数组成员.它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该 ...
- 浅谈C#中的for循环与foreach循环
for循环和foreach循环其实可以算得上是从属关系的,即foreach循环是可以转化成for循环,但是for循环不一定能转换成foreach循环. 下面简单介绍一下两种循环: 1.for循环 代码 ...
- Java中ArrayList的fori和foreach效率比较
1. list的元素为Integer [代码实例1] public static void main(String[] args) { List<Integer> list = new A ...
随机推荐
- 北京市行政村边界shp数据/北京市乡镇边界/北京市土地利用分类数据/北京市气象数据/降雨量分布数据/太阳辐射数据
数据下载链接:数据下载链接 北京是一座有着三千多年历史的古都,在不同的朝代有着不同的称谓,大致算起来有二十多个别称.北京地势西北高.东南低.西部.北部和东北部三面环山,东南部是一片缓缓向渤海倾斜的 ...
- 问题:CondaHTTPError: HTTP 000 CONNECTION FAILED for url <https://mirrors.tuna.tsinghua.edu.cn/anaconda/pk
使用anaconda安装tensorflow (windows10环境) 遇到的问题:CondaHTTPError: HTTP 000 CONNECTION FAILED for url <ht ...
- SDK导入问题 __imp_与__imp__
目前刚刚实习一周,接触的第一个项目是CMake编译的QT项目,需要引入公司的SDK,编译能过去但是程序就是找不到SDK的接口, 排查了半天发现问题在于:公司的SDK是32位的,自己项目的build k ...
- datax开启hana支持以及dolphinscheduler开启datax任务
datax开启hana支持以及dolphinscheduler开启datax任务 前面(@,@) 前段时间因为要做异构数据导入导出,所以搜了下,发现这类工具收费的居多,使用起来未必趁手~ 于是我找了下 ...
- SpringBoot接口 - 如何生成接口文档之非侵入方式(通过注释生成)Smart-Doc?
通过Swagger系列可以快速生成API文档,但是这种API文档生成是需要在接口上添加注解等,这表明这是一种侵入式方式: 那么有没有非侵入式方式呢, 比如通过注释生成文档? 本文主要介绍非侵入式的方式 ...
- 聊聊 C++ 中的四种类型转换符
一:背景 在玩 C 的时候,经常会用 void* 来指向一段内存地址开端,然后再将其强转成尺度更小的 char* 或 int* 来丈量一段内存,参考如下代码: int main() { void* p ...
- OGC WebGIS 常用服务标准(WMS/WMTS/TMS/WFS)速查
本文只介绍实际工作中常用的 WMS.WMTS.WFS.TMS 四种,WCS.WPS 等其它 OGC WebService 类型请自行查阅官方资料. 目录 0. 参数传递方式 1. WMS 速查 1.1 ...
- 2022-07-21 第四组 java之继承
目录 一.继承 1.概念 2.语法 3.父类成员访问 3.1 子类访问父类的成员变量 3.1.1 子类和父类中不存在同名的成员变量 3.1.2 子类和父类中不存在同名的成员变量 3.2 子类中访问父类 ...
- YII学习总结5(视图)
<?php namespace app\controllers; use yii\web\Controller; class HelloController extends Controller ...
- 利用Css3样式属性Cursor来更换自定义个性化鼠标指针(光标)
现而今,我们纵向的回顾整个大前端的历史,不难发现,人们对前端的审美要求越来越高,越来越严苛,与此同时,人们对追求美的体验是也极致的,从理性到感性,从平面到几何,从现实到虚拟,所以从某种角度来说,作为前 ...