for和foreach 的效率问题是个老问题了,从网上看到的是众说纷纭,有说for效率高的也有说foreach效率高的,还有说测试方法有问题的;鉴于此,我就自己做了个试验证明一下,然后探究一下可能的原因。
先看测试结果:

我的测试结果是for的效率要比foreach高出一截来。
再看测试代码(如果大家觉得我的测试有问题,请提出来呀):

forVSforeach
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;

namespace ForVSForEach


{
class Program

{
const int ARRAY_LENGTH = 9000000;

static void Main(string[] args)

{
ForVSForEach(8);

ForVSForEach(new Person());

Console.Read();
}


static void ForVSForEach<T>(T type) where T:new()

{
T[] array = new T[ARRAY_LENGTH];
for (int i = 0; i < array.Length; i++)

{
array[i] = new T();
}

Stopwatch watch = new System.Diagnostics.Stopwatch();
T tempFor = default(T);
watch.Start();
for (int i = 0; i < array.Length; i++)

{
tempFor = array[i];
}
watch.Stop();

long forTicks = watch.ElapsedTicks;

T tempForeach = default(T);
watch.Reset();
watch.Start();
foreach (T i in array)

{
tempForeach = i;
}
watch.Stop();
long forEachTicks = watch.ElapsedTicks;

//Console.WriteLine(tempFor);
Console.WriteLine("使用的类型:{0},循环次数:{1}",typeof(T),ARRAY_LENGTH);

Console.WriteLine("for use time {0}", forTicks);

Console.WriteLine("foreach use time {0}", forEachTicks);


}

}

class Person

{

public Person()
{ }

private int _id;

public int ID

{

get
{ return _id; }

set
{ _id = value; }
}

private string _name;

public string Name

{

get
{ return _name; }

set
{ _name = value; }
}

}
}

原因分析:
为了使一个类实例可以被foreach 需要继承 IEnumerable 接口,而此接口只有一个方法,返回一个IEnumerator的接口实例,这就说明foreach是通过操纵IEnumerator的方法来实现的。我们再看IEnumerator的两个方法和一个属性:
属性:object Current {get;}
方法:bool MoveNext()
void Reset()
我们可以推断一下foreach的实现,下面是伪代码:(下面的假设是错误的)
IEnumerator enumerator = IEnumerableInstance. GetEnumerator();
Object foreachObj = null;
enumerator.Reset(); //有可能会在此处执行一下Reset以保证从集合的第一个元素开始foreach。

foreachStart :
if (enumerator.MoveNext())


{
foreachObj = enumerator.Current;

{

//this area is the foreach block
}
goto foreachStart;
}
foreachEnd:

让我们再分析一下for(int i=0;i<array.length;i++)的可能执行过程:
int i = 0;
forStart:
if(i<array.length)



{


{
//this area is the for block

}

goto forStart;
}
forEnd:

很显然for的执行过程中少了几个方法的执行,而且要少一些额外的步骤,这是不是for效率比foreach稍高一筹的原因呢?由于以上推断纯属个人推断,所以还不敢下结论,请大家帮忙找一下理论根据。
最后:
虽然说for比foreach效率稍微高一点,但是foreach的更优雅一点,另外for除了效率之外也有一个优点就是我们可以在执行for的过程中更新,删除集合的元素值,而在foreach中这是不允许的。
如果既可以使用for也可以使用foreach的时候,我们可以使用foreach使代码优雅一点,除非这段代码需要特别注意性能。
static void One() { int[] a = new int[10000]; foreach(int i in a) Console.WriteLine(i); }
|
static void Two() { int[] a = new int[10000]; for(int j=0; j<a.Length; j++) { int i = a[j]; Console.WriteLine(i); } }
|
.method private hidebysig static void One() cil managed { // Code size 38 (0x26) .maxstack 2 .locals init (int32[] V_0, int32 V_1, int32[] V_2, int32 V_3)
|
.method private hidebysig static void Two() cil managed { // Code size 36 (0x24) .maxstack 2 .locals init (int32[] V_0, int32 V_1, int32 V_2)
|
IL_0000: ldc.i4 0x2710 IL_0005: newarr [mscorlib]System.Int32 IL_000a: stloc.0 IL_000b: ldloc.0 IL_000c: stloc.2 IL_000d: ldc.i4.0 IL_000e: stloc.3 IL_000f: br.s IL_001f
|
IL_0000: ldc.i4 0x2710 IL_0005: newarr [mscorlib]System.Int32 IL_000a: stloc.0 IL_000b: ldc.i4.0 IL_000c: stloc.1 IL_000d: br.s IL_001d
|
IL_0011: ldloc.2 IL_0012: ldloc.3 IL_0013: ldelem.i4 IL_0014: stloc.1 IL_0015: ldloc.1 IL_0016: call void [mscorlib]System.Console::WriteLine(int32) IL_001b: ldloc.3 IL_001c: ldc.i4.1 IL_001d: add IL_001e: stloc.3 IL_001f: ldloc.3 IL_0020: ldloc.2 IL_0021: ldlen IL_0022: conv.i4 IL_0023: blt.s IL_0011
|
IL_000f: ldloc.0 IL_0010: ldloc.1 IL_0011: ldelem.i4 IL_0012: stloc.2 IL_0013: ldloc.2 IL_0014: call void [mscorlib]System.Console::WriteLine(int32) IL_0019: ldloc.1 IL_001a: ldc.i4.1 IL_001b: add IL_001c: stloc.1 IL_001d: ldloc.1 IL_001e: ldloc.0 IL_001f: ldlen IL_0020: conv.i4 IL_0021: blt.s IL_000f
|
IL_0025: ret } // end of method MyClass::One
|
IL_0023: ret } // end of method MyClass::Two
|
上面的表格是for和foreach编译出来的IL代码,蓝色部分是循环的部分,执行步骤完全相同,所以理论上应该for和foreach的性能应该是一样的。
大家可以参考下面的三篇文章:
http://blogs.msdn.com/kevin_ransom/archive/2004/04/19/116072.aspx
http://blogs.msdn.com/brada/archive/2004/04/29/123105.aspx
http://www.cnblogs.com/WuCountry/archive/2007/02/27/658710.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架