foreach循环消除边界检查的特殊情况是什么?

foreach / for循环消除边界检查的特殊情况是什么? 还有哪些界限检查呢?

标准

 for(int i = 0; i < array.Length; i++) { ... } 

循环是允许JIT安全地删除数组边界检查(索引是否在[0..length-1]内)的循环

数组上的foreach循环等效for循环数组的标准。

编辑:罗伯特杰普森指出:

如果arrays是本地的,这将被优化。 如果可以从其他位置访问该数组,则仍将执行边界检查。 参考: arrays边界检查CLR中的消除

谢谢! 不知道自己。

SealedSun是对的。 不要优化你在C ++中的方式。 JIT非常聪明,可以为您做正确的事。 您始终可以以不同的方式对循环进行编码,然后检查IL代码。

  static void Main(string[] args) { int[] array = new int[100]; 00000000 push edi 00000001 push esi 00000002 push eax 00000003 xor eax,eax 00000005 mov dword ptr [esp],eax 00000008 mov edx,64h 0000000d mov ecx,79174292h 00000012 call 49E73198 00000017 mov esi,eax int sum = 0; 00000019 xor edx,edx 0000001b mov dword ptr [esp],edx for(int index = 0; index < array.Length; index++) 0000001e mov edi,dword ptr [esi+4] 00000021 test edi,edi 00000023 jle 00000033 { sum += array[index]; 00000025 mov eax,dword ptr [esi+edx*4+8] 00000029 add dword ptr [esp],eax for(int index = 0; index < array.Length; index++) 0000002c add edx,1 0000002f cmp edi,edx 00000031 jg 00000025 } Console.WriteLine(sum.ToString()); 00000033 mov esi,dword ptr [esp] 00000036 call 493765F8 0000003b push eax 0000003c mov ecx,esi 0000003e xor edx,edx 00000040 call 49E83A8B 00000045 mov edi,eax 00000047 mov edx,88h 0000004c mov ecx,1 00000051 call 49E731B0 00000056 mov esi,eax 00000058 cmp dword ptr [esi+70h],0 0000005c jne 00000068 0000005e mov ecx,1 00000063 call 4936344C 00000068 mov ecx,dword ptr [esi+70h] 0000006b mov edx,edi 0000006d mov eax,dword ptr [ecx] 0000006f call dword ptr [eax+000000D8h] 00000075 pop ecx } 00000076 pop esi 00000077 pop edi 00000078 ret 

现在,如果以C ++的方式优化代码,您将获得以下内容:

  static void Main(string[] args) { int[] array = new int[100]; 00000000 push edi 00000001 push esi 00000002 push ebx 00000003 push eax 00000004 xor eax,eax 00000006 mov dword ptr [esp],eax 00000009 mov edx,64h 0000000e mov ecx,79174292h 00000013 call 49E73198 00000018 mov esi,eax int sum = 0; 0000001a xor edx,edx 0000001c mov dword ptr [esp],edx int length = array.Length; 0000001f mov ebx,dword ptr [esi+4] for (int index = 0; index < length; index++) 00000022 test ebx,ebx 00000024 jle 0000003B 00000026 mov edi,dword ptr [esi+4] { sum += array[index]; 00000029 cmp edx,edi <-- HERE 0000002b jae 00000082 <-- HERE 0000002d mov eax,dword ptr [esi+edx*4+8] 00000031 add dword ptr [esp],eax for (int index = 0; index < length; index++) 00000034 add edx,1 00000037 cmp edx,ebx 00000039 jl 00000029 } Console.WriteLine(sum.ToString()); 0000003b mov esi,dword ptr [esp] 0000003e call 493765F8 00000043 push eax 00000044 mov ecx,esi 00000046 xor edx,edx 00000048 call 49E83A8B 0000004d mov edi,eax 0000004f mov edx,88h 00000054 mov ecx,1 00000059 call 49E731B0 0000005e mov esi,eax 00000060 cmp dword ptr [esi+70h],0 00000064 jne 00000070 00000066 mov ecx,1 0000006b call 4936344C 00000070 mov ecx,dword ptr [esi+70h] 00000073 mov edx,edi 00000075 mov eax,dword ptr [ecx] 00000077 call dword ptr [eax+000000D8h] 0000007d pop ecx } 0000007e pop ebx 0000007f pop esi 00000080 pop edi 00000081 ret 00000082 call 4A12746B 00000087 int 3 

顺便说一句 - 这与foreach声明是一样的:

  static void Main(string[] args) { int[] array = new int[100]; 00000000 push edi 00000001 push esi 00000002 push eax 00000003 xor eax,eax 00000005 mov dword ptr [esp],eax 00000008 mov edx,64h 0000000d mov ecx,79174292h 00000012 call 49E73198 00000017 mov esi,eax int sum = 0; 00000019 xor edx,edx 0000001b mov dword ptr [esp],edx for(int index = 0; index < array.Length; index++) 0000001e mov edi,dword ptr [esi+4] 00000021 test edi,edi 00000023 jle 00000033 { sum += array[index]; 00000025 mov eax,dword ptr [esi+edx*4+8] 00000029 add dword ptr [esp],eax for(int index = 0; index < array.Length; index++) 0000002c add edx,1 0000002f cmp edi,edx 00000031 jg 00000025 } Console.WriteLine(sum.ToString()); 00000033 mov esi,dword ptr [esp] 00000036 call 493765F8 0000003b push eax 0000003c mov ecx,esi 0000003e xor edx,edx 00000040 call 49E83A8B 00000045 mov edi,eax 00000047 mov edx,88h 0000004c mov ecx,1 00000051 call 49E731B0 00000056 mov esi,eax 00000058 cmp dword ptr [esi+70h],0 0000005c jne 00000068 0000005e mov ecx,1 00000063 call 4936344C 00000068 mov ecx,dword ptr [esi+70h] 0000006b mov edx,edi 0000006d mov eax,dword ptr [ecx] 0000006f call dword ptr [eax+000000D8h] 00000075 pop ecx } 00000076 pop esi 00000077 pop edi 00000078 ret 

不要在没有数字的情况下尝试优化代码。 正如你所看到的,如果你不采取行动,JIT会为你做很多事情。 在优化之前使用分析器。 总是。

有关详细信息,请参见

http://codebetter.com/blogs/david.hayden/archive/2005/02/27/56104.aspx

基本上,如果你有一个for循环,并且你明确地引用了IList.Count或Array.Length,JIT将捕获它,并跳过边界检查。 它比预先计算列表长度更快。

我相信,在列表或数组上的foreach将在内部执行相同的操作。

foreach循环使用枚举器,它是处理循环的类或结构。 枚举器具有Current属性,该属性返回集合中的当前项。 这消除了使用索引来访问集合中的项目,因此不需要获取项目的额外步骤,包括边界检查。

什么? 我不确定是否有可能消除c#中的边界检查。 如果您想要非托管代码,请使用:

 int[] array; fixed (int * i = array) { while (i++) Console.WriteLine("{0}", *i); } 

例如 – 它不检查边界,并且非常死亡。 🙂