您的位置:首页 > 其它

深入了解 foreach 和 for 循环到底有哪些不同 (一)

2008-08-20 21:14 483 查看
看到有些博客谈到for 循环的性能要好于 foreach。那么到底是不是呢?如果是的话,究竟两者之间有什么区别? Jitted的代码可能是最直接的方式来了解两者的差异。

例子代码:

namespace Test

{

class Sample

{

private static int counter;

public int Value = counter++;

}

class Program

{

static void Main(string[] args)

{

List<Sample> samples = new List<Sample>()

{

new Sample(),

new Sample(),

new Sample()

};

ForeachTest(samples);

ForTest(samples);

Console.Read();

}

private static void ForeachTest(List<Sample> samples)

{

foreach (Sample item in samples)

{

Console.WriteLine(item.Value);

}

}

private static void ForTest(List<Sample> samples)

{

for (int i = 0; i < samples.Count; i++)

{

Console.WriteLine(samples[i].Value);

}

}

}

}

环境准备:

新建一个c#命令行工程,然后拷贝例子代码。

选择Release模式。

Ctrl+F5在VS环境直接运行程序但不进入调试模式。

打开windbg, F6来attach刚才运行起来的程序。

Windbg:

.loadby sos mscorwks 加载sos

先来看一下 for 循环到底有什么。

0:000> !name2ee test Test.Program.ForTest

Module: 00192c5c (Test.exe)

Token: 0x06000004

MethodDesc: 00193014

Name: Test.Program.ForTest(System.Collections.Generic.List`1<Test.Sample>)

JITTED Code Address: 00a90208

反编译出ForTest的jitted代码,方便大家阅读我已经放入了较详细的注释。

0:000> !u 00a90208

Normal JIT generated code

Test.Program.ForTest(System.Collections.Generic.List`1<Test.Sample>)

Begin 00a90208, size 4d

>>> 00a90208 55 push ebp

004001f9 8bec mov ebp,esp

004001fb 57 push edi

004001fc 56 push esi

004001fd 53 push ebx

004001fe 8bd9 mov ebx,ecx //ecx 保存 samples 对象

00400200 33ff xor edi,edi //edi 清零

00400202 8b430c mov eax,dword ptr [ebx+0Ch] //设置List的大小到eax,就是3

00400205 85c0 test eax,eax

00400207 7e31 jle 0040023a //如果上句测试失败,跳转到结束

00400209 3bf8 cmp edi,eax //循环的开始。进行循环比较,edi是当前的相等于变量i的值,eax是3

0040020b 7205 jb 00400212 //如果已经超过范围,直接抛出OutOfRangeException,否则跳过下一句

*** WARNING: Unable to verify checksum for C:"Windows"assembly"NativeImages_v2.0.50727_32"mscorlib"9adb89fa22fd5b4ce433b5aca7fb1b07"mscorlib.ni.dll

0040020d e872a6bc66 call mscorlib_ni+0x68a884 (66fca884) (System.ThrowHelper.ThrowArgumentOutOfRangeException(), mdToken: 060000d9)

00400212 8b4304 mov eax,dword ptr [ebx+4] //传送List对象内部数组的地址到eax

00400215 3b7804 cmp edi,dword ptr [eax+4] //范围校验

00400218 7325 jae 0040023f //失败跳转到JIT_RngChkFail

0040021a 8b44b80c mov eax,dword ptr [eax+edi*4+0Ch] //传送第i个sample对象的地址到eax

0040021e 8b7004 mov esi,dword ptr [eax+4] //传送第i Sample个对象的Value值进入esi

00400221 e87ad07666 call mscorlib_ni+0x22d2a0 (66b6d2a0) (System.Console.get_Out(), mdToken: 06000772)

00400226 8bc8 mov ecx,eax //传送 Console.Out 地址到 ecx

00400228 8bd6 mov edx,esi //传送第i Sample个对象的Value值进入 edx

0040022a 8b01 mov eax,dword ptr [ecx] //传送Console.Out 的method table到 eax

0040022c ff90bc000000 call dword ptr [eax+0BCh] //打印Sample.Value到控制台程序

00400232 47 inc edi //edi加1

00400233 8b430c mov eax,dword ptr [ebx+0Ch] //设置List的大小到eax,就是3

00400236 3bc7 cmp eax,edi //检查是否需要继续循环

00400238 7fcf jg 00400209 //跳转到继续循环代码处如果上步为真

0040023a 5b pop ebx

0040023b 5e pop esi

0040023c 5f pop edi

0040023d 5d pop ebp

0040023e c3 ret

00a9024f e868be7067 call mscorwks!JIT_RngChkFail (6819c0bc)

00a90254 cc int 3

从代码中可以看到,循环开始处进行一次比较,如果失败则抛出OutOfRangeException。最后在结尾处也进行一次判断来决定是否继续循环。当中的代码比较直观,基本上就是取值然后打印输出。当然还有inc来累加计数。

下一次,我们来看看ForeachTest到底有些什么。哪些和ForTest不一样。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: