这段代码居然运行正常

 

乍一看还以为这个世界变得太快自己不认识了,再一琢磨,原来是凑巧而已,世界虽然变了点,但基本的东西还是没变的。

              ArrayList test = new ArrayList();

              int i;

 

              for(i = 0;i < 20;i++)

                   test.Add(i);

 

              for (i = 0;i < test.Count;i++)

                   if (i % 2 != 0)   

                       test.RemoveAt(i);

 

              for(i = 0;i < test.Count;i++)

                   Console.WriteLine("Array [{0}] is {1}",i,test[i]);          

          Console.Read();

这种类型的代码在Delphi下别说是运行结果正常,根本就是连运行都运行不了的,最后肯定会报出来一个“List index out of bounds()”这样的错误。

 

首先是它运行不报错,说明For循环与在delphi下是不太一样的。

Delphi代码:

    for i := 0 to test.Count - 1 do

      if (i mod 2) <> 0 then

        test.Delete(i);

它的for循环是从一开始的时候就把初始与终止条件定好了,在循环过程中其终止条件的值是不变的(不知道如何把DelphiCPU视图里的ASM代码拷出来,截个图算了)

看最底部的两句dec esijnz -$20。在最开始进入循环的时候这个esi就定了。所以说在循环体内虽然有test.Delete(i)这样的语句,但其由test.Count – 1计算得的终止条件没有变,它在最开始的时候就已经把它取出来(当然它不是取终止条件,实际上是直接把循环次数算出来给了esi的,把循环初始条件改为非0开始能看得更明白点)。所以上面这种从头开始删的方法是会报错的。

 

而在C#里,上面那段代码查看IL

  IL_0036:          callvirt    instance         int32       [mscorlib]System.Collections.ArrayList::get_Count()

  IL_003b:        blt.s      IL_0024

类似的语句。这里的for循环在执行过程中其终止条件是一直在变化的(这里for的流程决定了其for循环的强大之处;Delphi里的那个for本身的语法没有C/C#里的这么复杂,所以它的编译器作了点优化。PSDelphi的虽然比较的简洁一点,但有时也太不方便,比如无法控制步长)。你删尽管删好了,反正它的i只到当时test.Count那个地方就停了。所以上面的代码可以运行。

 

至于运行正常那正好是凑巧而已了。每次RemoveAt掉一个使ArrayList后部的元素都上提的同时而i又增1,由此每次RemoveAtfor循环遍历漏掉一个元素的情况下,正好那个漏掉的元素本身是不符合条件的,反正漏掉它也无关痛痒。所以上面的运行结果看起来很正常。其实把

               if (i % 2 != 0)   

改为

               if (i % 3 != 0)   

就可以看出结果不正常了,当然if (i % 3 == 0)  这样的条件出来结果也是正常的,也是因为漏掉的元素无关痛痒的缘故。

当然即使能运行正常也不要写上面那样的代码好,只有20个元素看不太出来,如果元素一多就严重影响性能了啦。

 

还好世界其实并没有变。只是一个for不同而已,删东西还是从尾部开始删。

              for(i = test.Count - 1;i >=0;i--)

                   if (i % 3 != 0)   

                   test.RemoveAt(i);

一切恢复正常,速度也飞快。

 

 

原文地址:https://www.cnblogs.com/lichdr/p/87184.html