简体   繁体   English

无法从XNA中的列表中删除项目

[英]Can't remove item from list in XNA

I'm working on a Flappy Bird clone as an exercise, for I've recently started programming on XNA and I'm stuck on this error that I cannot understand. 我正在研究一个Flappy Bird克隆作为练习,因为我最近开始在XNA上编程,而且我遇到了这个我无法理解的错误。

I included the following code inside the Update() function, which's job is to delete the pipes when they go off screen lest they infinitely move leftwards while more are created: 我在Update()函数中包含了以下代码,其作用是在屏幕离开时删除管道,以免在创建更多内容时向左无限移动:

//Pipe despawner
foreach (var pipe in pipes)
{
   if (pipe.position1.X <= -180)
   {
         pipes.Remove(pipe);
   }
}

The game runs fine until the first pipe goes offscreen and the debugger pauses and signals this part of the code with the following message: 游戏运行正常,直到第一个管道离开屏幕,调试器暂停并通过以下消息发出这部分代码的信号:

An unhandled exception of type 'System.InvalidOperationException' occurred in mscorlib.dll

Additional information: Colección modificada; puede que no se ejecute la operación de enumeración.

I'm sorry the second part is in spanish, it's my system's language, but hopefully you know how to solve the problem anyway. 对不起,第二部分是西班牙语,这是我系统的语言,但希望你知道如何解决问题。

I believe I could simply not include this part of the code, given the simplicity of the game and, by consequence, the small toll that infinitely spawning pipes take on performance, but I'd rather not adopt this practice as soon as I start learning game programming. 考虑到游戏的简单性,我认为我可以简单地不包括这部分代码,结果是无限产生的管道对性能造成的影响很小,但我不想在我开始学习时立即采用这种做法。游戏编程。

The problem is that you are modifying the collection (by removing a pipe) while enumerating the collection. 问题是您在枚举集合时修改集合(通过删除管道)。 This will always throw an exception in .NET (and is something to be mindful of if you ever do this in a multi-threaded environment, since you can get some nastily non-reproducible exceptions because of it). 这将始终在.NET中引发异常(如果您在多线程环境中执行此操作,则需要注意,因为您可以获得一些由于它而无法重现的异常)。

An easy way to solve it is to just keep a "kill" list while enumerating: 解决它的一个简单方法是在枚举时保留“kill”列表:

List killList = new List(); 列表killList = new List();

foreach (var pipe in pipes)
{
   if (pipe.position1.X <= -180)
   {
      killList.Add(pipe)
   }
}

Even easier: 更简单:

IEnumerable<Pipe> killList = pipes.Where (p => p.position1.X < -100);

Either way, enumerate over this new collection, and remove matching elements from the main one (thus avoiding the error condition): 无论哪种方式,枚举这个集合,并从主要元素中删除匹配元素(从而避免错误条件):

foreach (Pipe p in killList)
   pipes.Remove(p);

And you are done! 你完成了! Again, be mindful of threads. 再次,请注意线程。 If you are creating new pipes in another thread, you could easily "collide" with this one and cause the exception. 如果要在另一个线程中创建新管道,则可能很容易与此管道“发生碰撞”并导致异常。 If that is the case, make sure to put locks around these sections of code. 如果是这种情况,请确保在这些代码段周围放置锁。

Note, you could actually inline "killList" if you use the LINQ method: 注意,如果使用LINQ方法,实际上可以内联“killList”:

foreach (Pipe p in pipes.Where(p => p.Position1.X <= -100))
   pipes.Remove(p);

As @rot13 suggested, you could also just run RemoveAll, passing it the predicate from the "Where" statement like: 正如@ rot13建议的那样,你也可以运行RemoveAll,从“Where”语句中传递谓词,如:

pipes.RemoveAll(p => p.Position1.X <= -100);

That's about as simple as it gets :) 这就像它得到的一样简单:)

You can't remove an item from the collection you're currently iterating on with foreach . 您无法从当前使用foreach迭代的集合中删除项目。

Either use a for loop and adjust the indexes yourself, or use a second collection to handle the pipes you want to delete : 要么使用for循环并自己调整索引,要么使用第二个集合来处理要删除的管道:

List<Pipe> piesToRemove = new List<Pipe>();
// First you flag every pipes you need to remove
foreach (var pipe in pipes)
{
   if (pipe.position1.X <= -180)
   {
         pipesToRemove.Add(pipe);
   }
}

//Now you remove them from your original collection
foreach (var pipe in pipesToRemove)
{
    pipes.Remove(pipe);
}

您可能只想将它们移动到屏幕的另一侧并向上/向下移动它们,而不是移除管道。

It's saying you cannot modify pipes while looping through it. 它说你在循环时不能修改管道。 Try something like this: 尝试这样的事情:

        //Pipe despawner
        var unusedPipes = new List<Pipe>();

        foreach (var pipe in pipes)
        {
            if (pipe.position1.X <= -180)
            {
                unusedPipes.Add(pipe);
            }
        }

        foreach (var unusedPipe in unusedPipes)
        {
            pipes.Remove(unusedPipe);
        }

I just took a guess that pipes is a list of pipes. 我只是猜测管道是一个管道列表。

The other answers are perfectly valid and will resolve your issue. 其他答案完全有效,将解决您的问题。

However, I think you should be aware that you don't need to create a second collection just to remove an object during iteration. 但是,我认为您应该知道,您不需要创建第二个集合只是为了在迭代期间删除对象。

We can use reverse iteration to remove an object from your collection. 我们可以使用反向迭代从集合中删除对象。 Try the following: 请尝试以下方法:

foreach (Pipe pipe in pipes.Reverse<Pipe>())
{
    pipes.Remove(pipe);
}

The above code makes the assumption that your collection is a generic List: 上面的代码假设您的集合是通用List:

List<Pipe> pipes = new List<Pipe>();

Edit 编辑

Here is a sample from the game I'm currently working on: 以下是我目前正在进行的游戏中的示例:

private List<Debris> listOfDebris = new List<Debris>();

foreach (Debris debris in listOfDebris.Reverse<Debris>())
{
    CalculateDebrisLocation(debris);

    // Remove debris if it travels outside level boundries
    if (!debris.Rect.Intersects(currentLevel.MapRect))
        listOfDebris.Remove(debris);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM