代码之家  ›  专栏  ›  技术社区  ›  Ed Guiness

为什么“猴子和循环索引”不好?

  •  15
  • Ed Guiness  · 技术社区  · 15 年前

    Steve McConnell的清单项目之一是 you should not monkey with the loop index (第16章,第25页, 循环索引 ,PDF格式)。

    这是很直观的,也是我一直遵循的一种实践,除了我在一天中学会了如何编程。

            for ( int i=0 ; i < this.MyControl.TabPages.Count ; i++ )
            {
                this.MyControl.TabPages.Remove ( this.MyControl.TabPages[i] );
                i--;
            }
    

    这几乎很有趣,因为它通过将索引保持在零直到所有TabPages都被删除来工作。

    这个循环可以写成

            while(MyControl.TabPages.Count > 0)
                MyControl.TabPages.RemoveAt(0);
    

    由于控件实际上是与循环同时写入的,所以它甚至可以作为

            MyControl.TabPages.Clear();
    

    从那以后,我一直受到关于代码审查问题的质疑,并发现我对 为什么? 这是一个坏习惯,它没有我所希望的那么强烈。我说过,更难理解循环的流程,因此更难维护和调试,最终在代码的生命周期中更昂贵。

    11 回复  |  直到 6 年前
        1
  •  24
  •   PEZ    15 年前

    我认为你的发音很棒。也许可以这样说:

    应该更清楚。

        2
  •  23
  •   Marc Gravell    15 年前

    好吧,这只会增加一点混乱——你也可以很容易地写:

    while(MyControl.TabPages.Count > 0)
    {
        MyControl.TabPages.Remove(MyControl.TabPages[0]);
    }
    

    或(更简单)

    while(MyControl.TabPages.Count > 0)
    {
        MyControl.TabPages.RemoveAt(0);
    }
    

    或(最简单)

    MyControl.TabPages.Clear();
    

        3
  •  16
  •   Toon Krijthe    15 年前

    这都是期待。

    如果你弄乱了循环计数器(或者猴子,如果你喜欢的话),你的循环就不会像预期的那样运行。这意味着它更难理解,并且增加了代码被误解的机会,这会引入bug。 或者(错误地)引用一个聪明但虚构的人物:

    complexity leads to misunderstanding
    
    misunderstanding leads to bugs
    
    bugs leads to the dark side.
    
        4
  •  3
  •   Sam Meldrum    15 年前

    for ( int i=0 ; i < this.MyControl.TabPages.Count ; i++ ) {
        this.MyControl.TabPages.Remove ( this.MyControl.TabPages[i] );
        i--;
    }
    

    减少如下:

    for ( int i=0 ; i < this.MyControl.TabPages.Count ; ) {
        this.MyControl.TabPages.Remove ( this.MyControl.TabPages[i] );
    }
    

    然后:

    for ( ; 0 < this.MyControl.TabPages.Count ; ) {
        this.MyControl.TabPages.Remove ( this.MyControl.TabPages[0] );
    }
    

    但是 虽然 环还是环 清除() 方法(如果存在的话)显然更可取。

        5
  •  2
  •   Paul Dixon    15 年前

    我认为你可以通过引用Knuth的 literate programming ,该计划应 其他程序员 ,因此更简单的循环:

     while (this.MyControl.TabPages.Count>0)
     {
                this.MyControl.TabPages.Remove ( this.MyControl.TabPages[0] );
     }
    

        6
  •  1
  •   foxy    15 年前

    这可能更清楚:

    while (this.MyControl.TabPages.Count > 0)
    {
      this.MyControl.TabPages.Remove ( this.MyControl.TabPages[0] );
    }
    
        7
  •  1
  •   Rad    15 年前

    可以使用的一个论点是调试这样的代码要困难得多,因为索引被更改了两次。

        8
  •  1
  •   Joris Timmermans    15 年前

    简言之,这是不必要的聪明,它增加而不是消除冗余。因为它可以明显更简单,也可以明显更短,所以它是错误的。

        9
  •  1
  •   supercat    14 年前

    如果有人有选择地删除某些内容,那么就根本不用担心索引了。即使在这种情况下,我认为最好说:

      i=0;
      while(i < MyControl.Tabpages.Count)
        if (wantToDelete(MyControl.Tabpages(i))
          MyControl.Tabpages.RemoveAt(i);
        else
          i++;
    而不是每次删除后都将循环索引。或者,更好的是,让索引计数向下,这样当一个项目被删除时,它不会影响未来需要删除的项目的索引。如果删除了许多项目,这也有助于减少每次删除后移动项目所花费的时间。
        10
  •  0
  •   Matthew Pelser    15 年前

    我想指出这样一个事实,即循环迭代不是像任何人所期望的那样由“I++”控制,而是由疯狂的“I--”设置控制,这就足够了。