深入了解 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不一样。
例子代码:
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不一样。
相关文章推荐
- 深入了解 foreach 和 for 循环到底有哪些不同 (二)
- 深入了解 JavaScript 中的 for 循环
- 深入了解 JavaScript 中的 for 循环
- 深入了解java8的foreach循环
- 深入 foreach 和 for 循环的区别
- 深入理解java中for和foreach循环
- 深入了解 JavaScript 中的 for 循环
- 深入了解 JavaScript 中的 for 循环
- 深入了解 JavaScript 中的 for 循环
- 深入了解 JavaScript 中的 for 循环 – 码农网
- 深入 foreach 和 for 循环的区别
- 深入了解 JavaScript 中的 for 循环
- 深入for,while,foreach遍历时间比较的详解
- Java for循环和foreach循环的性能比较
- 深入了解Windows句柄到底是什么
- php循环语句 for()与foreach()用法区别介绍
- for ,foreach ,map 循环的区别
- JAVA foreach和普通for循环是否需要判断为null
- Java中的增强 for 循环 foreach+移除元素