XNA Creators Club Online
Page 1 of 1 (10 items)
Sort Posts: Previous Next

Exception : "Collection was modified; enumeration operation may not execute."

Last post 7/16/2009 6:08 PM by mpipe. 9 replies.
  • 7/14/2009 4:16 PM

    Exception : "Collection was modified; enumeration operation may not execute."

    Hey Everyone,

    I'm running into a strange exception while dealing with enumerators, in C#. The odd part is that it is inconsistent as to when the exception is thrown. I'll post clips of my code to help explain.

    I have CheckCollisions() which runs in Update() and works perfectly fine when I collide with a gc that is a Wall.
    but, I get the exception when I collide with a gc that is a Plant. (in line 5 at "in")

    1 protected void CheckCollisions() 
    2         { 
    3             bool hasCollision = false
    4  
    5             foreach (GameComponent gc in Components) 
    6             { 
    7                 if (gc is Wall) 
    8                 { 
    9                     hasCollision = ((Wall)gc).CircleCollides(player); 
    10                     if (hasCollision) 
    11                     { 
    12                         Vector2 pVelocity = player.GetVelocity(); 
    13                         Vector2 pPosition = player.GetPosition(); 
    14                         player.SetPosition(pPosition-pVelocity); 
    15                         player.SetVelocity(-pVelocity *0.2f);                         
    16                     } 
    17                 } 
    18                   
    19                 else if (gc is Plant) 
    20                 { 
    21                     hasCollision = ((Plant)gc).CircleCollides(player); 
    22                     if (hasCollision) 
    23                     { 
    24                         int current = player.GetPlant(); 
    25                         int value = ((Plant)gc).GetValue(); 
    26                         player.SetPlant(current + value); 
    27                         gc.Dispose(); 
    28                     } 
    29                 } 
    30

    The interesting part, is that they is very little difference between the two classes. Both are created in a similar fashion and both are displaying properly on the screen.

    1 // Spawn Walls on right click 
    2             if (player != null && mouse.RightButton == ButtonState.Pressed && lastmouse.RightButton == ButtonState.Released) 
    3             { 
    4                 Vector2 wallPosition = new Vector2((mouse.X - 32), (mouse.Y - 32)); 
    5                 SpawnWall(wallTexture, wallSize, wallPosition); 
    6             } 
    7               
    8              
    9             // Spawn plant on left control 
    10             if (player != null && keyboard.IsKeyDown(Keys.LeftControl) && lastkeyboard.IsKeyUp(Keys.LeftControl)) 
    11             { 
    12                 Vector2 plantPosition = new Vector2((mouse.X - 8), (mouse.Y - 8)); 
    13                 Vector2 still = new Vector2(0, 0); 
    14                 SpawnPlant(plantTexture, plantSize, plantPosition, still, 1); 
    15             } 
    ...
     public Wall SpawnWall(Texture2D texture, Vector2 size, Vector2 position) 
            { 
                Wall wall = new Wall(thisref texture, size, position); 
                Components.Add(wall); 
                return wall; 
            } 
     
            public Plant SpawnPlant(Texture2D texture, Vector2 size, Vector2 position, Vector2 velocity, int value) 
            { 
                Plant plant = new Plant(thisref texture, size, position, velocity, value); 
                Components.Add(plant); 
                return plant; 
            } 

    I have checked the enumeration list while running and both are being added properly, but only when I collide with a Plant, does it produce the exception.

    I'm sure it is something simple, but I am not that experienced with C#.

    Thank you for your help!
    -Matthew Church

    here is the extra info if you want:
    System.InvalidOperationException was unhandled
      Message="Collection was modified; enumeration operation may not execute."
      Source="mscorlib"
      StackTrace:
           at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
           at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
           at System.Collections.Generic.List`1.Enumerator.MoveNext()
           at PathfindingA.Game1.CheckCollisions() in C:\Users\User\Documents\Visual Studio 2008\Projects\PathfindingD\PathfindingA\Main\Game1.cs:line 210
           at PathfindingA.Game1.Update(GameTime gameTime) in C:\Users\User\Documents\Visual Studio 2008\Projects\PathfindingD\PathfindingA\Main\Game1.cs:line 170
           at Microsoft.Xna.Framework.Game.Tick()
           at Microsoft.Xna.Framework.Game.HostIdle(Object sender, EventArgs e)
           at Microsoft.Xna.Framework.GameHost.OnIdle()
           at Microsoft.Xna.Framework.WindowsGameHost.ApplicationIdle(Object sender, EventArgs e)
           at System.Windows.Forms.Application.ThreadContext.System.Windows.Forms.UnsafeNativeMethods.IMsoComponent.FDoIdle(Int32 grfidlef)
           at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
           at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
           at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
           at System.Windows.Forms.Application.Run(Form mainForm)
           at Microsoft.Xna.Framework.WindowsGameHost.Run()
           at Microsoft.Xna.Framework.Game.Run()
           at PathfindingA.Program.Main(String[] args) in C:\Users\User\Documents\Visual Studio 2008\Projects\PathfindingD\PathfindingA\Main\Program.cs:line 14
      InnerException:
  • 7/14/2009 4:37 PM In reply to

    Re: Exception : "Collection was modified; enumeration operation may not execute."

    Are you doing any multithreading by any chance? Not sure why else you would be getting that...

    You could copy the component list to another component collection, and loop through that so that there's no chance of that happening.

    [edit]: Just skimmed through your code again, and I see you're calling Dispose on the GameComponent when you collide with a Plant. That is most likely your problem. What I would do is set a "kill me" boolean to true if you collide with the plant, and after that loop, dispose anything with that flag on.

    Alternatively, you could use a for loop instead of a foreach loop.
    "Software is never finished, it is in varying states of 'less broken'" because "If it ain't broke, it doesn't have enough features yet"

    In Playtest: Avatar Land | The MANLY Game for MANLY Men

    The signature that was too big for the 512 char limit
  • 7/14/2009 4:50 PM In reply to

    Re: Exception : "Collection was modified; enumeration operation may not execute."

    Yep, UberGeekGames has it (and I had a whole post typed up explaining it before he jumped back in like a ninja and edited his original post to mention it! he's fast!). You're modifying the number of elements in your Collection of GameComponents by destroying one within the loop. That's a definite no-no.
  • 7/14/2009 4:54 PM In reply to

    Re: Exception : "Collection was modified; enumeration operation may not execute."

    Thanks guys, I haven't learned all the best C# practices yet :P

  • 7/15/2009 5:15 PM In reply to

    Re: Exception : "Collection was modified; enumeration operation may not execute."

    It's not a best practice. It's a requirement. Read the exception.
  • 7/16/2009 12:24 AM In reply to

    Re: Exception : "Collection was modified; enumeration operation may not execute."

    Just to add to the responses already provided.

    The over all problem in your code is as previously mentioned the Dispose() call while interating through the Components container with foreach. The reason the exception is being raised is due to you attempting to modify an instance of a GameComponent object contained in a container that does not allow modifications (In this case the copy of the Components container foreach is using to iterate with.)

    As suggested change the foreach loop to use a for loop instead and use an index and the RemoveAt() method exposed by the Components collection. This way if you still need to call to the Dispose() method on the GameComponent object first you can. Then the object can be correctly removed from the collection.

    Also as suggested you could wrap a class around the GameComponent object and add a boolean property to the new class that indicates that an object needs to be destroyed and removed from the Components container. Then at a later time or in a seperate thread you loop the collection again (using a for loop) and as described above the flagged objects would be removed using Remove() or RemoveAt() methods to remove them from the collection. Having to run a second loop to me seems like a lot more work than is required especially in a game but that is just my personal perspective.


    Sorry for piping in late just felt like the question asked had not really been answered and thought that maybe a little more information about why the exception was thrown and how to fix it would be appreciated.
    Are you Inspired?
    --------------------------
    Matthew Randall
    Casual Games Architect
    Exhale Game Studio
  • 7/16/2009 3:37 AM In reply to

    Re: Exception : "Collection was modified; enumeration operation may not execute."

    As was said in the above post, you can just use a for loop rather than a foreach loop. If you iterate from the end of the collection to the front, then you can safely remove items and alter the size of the list, without causing issues.

    Here's an example:
    for (int i = entityList.Count - 1; i > 0; i--) 
        { 
            if (entityList[i].MarkedForDeletion) 
                DestroyEntity(EntityList[i]); 
        } 

    XNA QuickStart Engine (3D Game Engine for XNA) | My site
    "I'll be whatever I want to do!", Philip J. Fry
  • 7/16/2009 5:38 AM In reply to

    Re: Exception : "Collection was modified; enumeration operation may not execute."

    Lord Ikon:

    for (int i = entityList.Count - 1; i >= 0; i--) 
        { 
            if (entityList[i].MarkedForDeletion) 
                DestroyEntity(EntityList[i]); 
        } 



    erm sorry for correction but i want to make sure he uses the correct code.
  • 7/16/2009 11:10 AM In reply to

    Re: Exception : "Collection was modified; enumeration operation may not execute."

    *puts on black ninja suit* ;-)

    Just wanted to point out that it's not necessary to use a reverse for loop in order to safely remove objects from it; you can also decrease the counter when you remove something to get the same effect:
    for(int i = 0; i < YourGameComponents.Count; i++) 
        // do stuff 
     
        if(NeedToRemoveThisGameComponent) 
        { 
            YourGameComponents.RemoveAt(i); 
            i--; 
        } 

    "Software is never finished, it is in varying states of 'less broken'" because "If it ain't broke, it doesn't have enough features yet"

    In Playtest: Avatar Land | The MANLY Game for MANLY Men

    The signature that was too big for the 512 char limit
  • 7/16/2009 6:08 PM In reply to

    Re: Exception : "Collection was modified; enumeration operation may not execute."

    for(int i = 0; i < YourGameComponents.Count; ) 
        // do stuff 
     
        if(NeedToRemoveThisGameComponent) 
        { 
            YourGameComponents.RemoveAt(i); 
        } else {
    i++;
    }
Page 1 of 1 (10 items) Previous Next
var gDomain='m.webtrends.com'; var gDcsId='dcschd84w10000w4lw9hcqmsz_8n3x'; var gTrackEvents=1; var gFpc='WT_FPC'; /*<\/scr"+"ipt>");} /*]]>*/
DCSIMG