问题 C#foreach在foreach循环中


我对C#没有太多经验,所以如果有人能指出我正确的方向,我会非常感激。我有一个foreach循环引用一个对象的变量。我希望在主要的一个中创建另一个foreach循环,它将当前变量与对象数组中其余变量进行比较(或执行操作)。 我有以下代码:

// Integrate forces for each body.
    foreach (RigidBodyBase body in doc.Bodies)
    {
        // Don't move background-anchored bodies.
        if (body.anchored) continue;

        // This is where we will add Each Body's gravitational force 
        //  to the total force exerted on the object.

        // For each other body, get it's point and it's mass.

            // Find the gravitational force exterted between target body and looped body.
                // Find distance between bodies.
                    // vector addition
                // Force = G*mass1*mass2/distance^2
            // Find vector of that force.
            // Add Force to TotalGravityForce
        // loop until there are no more bodies.
        // Add TotalGravityForce to body.totalForce

    }

6157
2017-12-01 17:42


起源



答案:


每次执行foreach时,(即使在嵌套它们的时候)内部枚举器应该为你“新”一个新的迭代器,应该没有任何问题。当您在迭代时添加或删除集合中的项目时会出现问题...

请记住,在内部的foreach中,要检查以确保您不在每个外部的同一项目上

  foreach( RigidBodyBase body in doc.Bodies)
     foreach ( RigidBodyBase otherBody in doc.Bodies)
         if (!otherBody.Anchored && otherBody != body)  // or otherBody.Id != body.Id -- whatever is required... 
              // then do the work here

顺便说一句,放置此代码的最佳位置是在RigidBodyBase类的GravityForce属性中,然后你可以写:

   foreach (RigidBodyBase body in doc.Bodies)
       body.TotalForce += body.GravityForce; 

虽然取决于你在这里所做的事情(移动所有物体?),但他们可能更有机会进行重构......我还考虑为“其他”力量设置一个单独的属性,并让TotalForce Property执行重力与“其他”力量相加?


15
2017-12-01 17:53



我最喜欢这个答案,因为根据我的阅读,foreach循环对for循环更有效。 - wcm


答案:


每次执行foreach时,(即使在嵌套它们的时候)内部枚举器应该为你“新”一个新的迭代器,应该没有任何问题。当您在迭代时添加或删除集合中的项目时会出现问题...

请记住,在内部的foreach中,要检查以确保您不在每个外部的同一项目上

  foreach( RigidBodyBase body in doc.Bodies)
     foreach ( RigidBodyBase otherBody in doc.Bodies)
         if (!otherBody.Anchored && otherBody != body)  // or otherBody.Id != body.Id -- whatever is required... 
              // then do the work here

顺便说一句,放置此代码的最佳位置是在RigidBodyBase类的GravityForce属性中,然后你可以写:

   foreach (RigidBodyBase body in doc.Bodies)
       body.TotalForce += body.GravityForce; 

虽然取决于你在这里所做的事情(移动所有物体?),但他们可能更有机会进行重构......我还考虑为“其他”力量设置一个单独的属性,并让TotalForce Property执行重力与“其他”力量相加?


15
2017-12-01 17:53



我最喜欢这个答案,因为根据我的阅读,foreach循环对for循环更有效。 - wcm


恕我直言应该是可能的,尽管你应该考虑Kibbee的建议。也许你也可以这样优化它(例如,像这样:)

int l = doc.Bodies.Count;
for ( int i = 0; i < l; i++ )
    for ( int j = i + 1; j < l; j++ )
        // Do stuff 

2
2017-12-01 17:51





只要你不改变内循环中的doc.Bodies,我就没有看到这个问题,因为这会导致事情爆发。但理论上这可行。至于优化,我不确定它是否最好,但它是可能的。


1
2017-12-01 17:54





嗯,这是一个O(n ^ 2)算法,但我想你在这里别无选择。如何将更多逻辑封装到另一个方法中。这使得简单易懂的东西更具可读性。

foreach (RigidBodyBase body in doc.Bodies)        
{                
   Integrateforces(ref body, Bodies);
}

...

public void Integrateforces(RigidBodyBase out body, RigidBodyBase[] Bodies)
{
  //Put your integration logic here
}

1
2017-12-01 17:58





在这种情况下,最好使用带有索引的常规for循环来表示你所在的元素。尝试使用它自己的foreach循环迭代同一个集合会导致问题。


-3
2017-12-01 17:46



我很确定你会得到一个带有第二个foreach循环的新枚举器,所以你不应该遇到任何问题。 - Jon Tackabury
是的,这是正确的,至少对于任何正确编写的集合类...一些编写自定义框架集合类的开发人员总是可能会错误地实现这一点,但对于来自MS或其他信誉良好的源的任何东西,这都不是问题。 - Charles Bretana
是的,对不起,我不知道今天的脑袋在哪里。虽然正如Charles所提到的那样,如果您在迭代时尝试更改集合(添加或删除),则会出现错误。 - Kibbee
如果你得到一个新的枚举器并不重要,重要的是你正在修改第一个foreach循环迭代的集合,从而导致错误。不是最好的解决方案,恕我直言,但+1 - Ricardo Villamil