0

I'm trying to build my unity project, however I can't as there is a problem with a foreach loop. Just below is the error code I'm receiving, but I don't understand it. Would anyone be able to explain what a possible a solution might be? Or why this error occurs?

InvalidOperationException: Collection was modified; enumeration operation may not execute. System.Collections.Generic.List`1+Enumerator[UnityEngine.Vector3].VerifyState () (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/List.cs:778)

System.Collections.Generic.List`1+Enumerator[UnityEngine.Vector3].MoveNext () (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/List.cs:784)

Player_Movement.Update () (at Assets/Scripts/Player/Player_Movement.cs:46)

To give context to be code you draw a line on the screen and it creates a list of vectors for the player to move through.

void Update()
{
    //when the player moves their finger accross the screen of the mouse button is held down
    if ((Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Moved) || (Input.GetMouseButton(0)))
    {
        //raycasthit variable called hit
        RaycastHit hit;
        //ray variable called ray
        Ray ray;
        //the ray variable is cast between the main camera and the mouse position
        ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        //if the physics racast hit then calulate the distance betwen the point (mouse position) and the camera
        if (Physics.Raycast(ray, out hit))
        {
            //save the positon as the nextPosition
            nextPosition = hit.point;
            //save the nextPositions point's yaxis 
            nextPosition.y = yAxis;
            //if there are positions inside of the position list
            if (positionList.Count != 0)
            {
                //then for each vector3 position in the list
        Line 46  ===> foreach(Vector3 t in positionList)
                {
                    //if there is no posiiton in the list where one should be
                    if (nextPosition != t)
                    {
                        //then create a position
                        positionList.Add(nextPosition);
                    }
                }
            }
            else
            {   //otherwise create a position in the position list
                positionList.Add(nextPosition);
            }
        }
    }
}
  • possible dup: [so1](http://stackoverflow.com/questions/604831/collection-was-modified-enumeration-operation-may-not-execute) [so2](http://stackoverflow.com/questions/2024179/collection-was-modified-enumeration-operation-may-not-execute-in-arraylist) – Lei Yang May 15 '17 at 01:42
  • 1
    In short, you should not be modifying a list (eg. With `Add()`) while you're iterating through it with a `foreach` loop. Try to restructure your logic so it won't need to do this. Also...not sure your code does what you think it'll do. Currently it'll add `nextPosition` to the list for **every** element in the list that doesn't match `nextPosition`...which doesn't seem right. – Serlite May 15 '17 at 01:47

2 Answers2

1

You can't modify a list (ie .Add() to it) while iterating over it.

Find your matching element, then add if necessary:

if(positionList.FirstOrDefault(t => nextPosition == t) != null)
    positionList.Add(nextPosition); 
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • Thank you so much for your response :) So with positionList.FirstOrDefault it doesn't seem to give me that functionality with lists. Is there a collection I should be using withing unity or does this functionality only work with lists of type int? – Chris Winning May 15 '17 at 03:20
  • Hey Mathias! Thank you again for your help :D I really appreciate it. I found out I should be using `using System.Linq` – Chris Winning May 15 '17 at 03:47
1

The reason that you're getting that exception is that you're trying to modify a collection while you're iterating over it. There are a few ways you can get around this, but probably the best way is to simply test your condition (does the collection contain a specific item) and then modify the collection (add the item) if the test is false.

The Linq extension Any comes in handy here. It returns true if any item in the collection matches the condition. Putting a ! in front of it means you're looking for the case where there are NOT Any items that match the condition:

if (!positionList.Any(position => position == nextPosition))
{
    positionList.Add(nextPosition);
}

But you can do this without Linq also, using the Contains method, which is actually simpler and arguably more readable:

if (!positionList.Contains(nextPostition))
{
    positionList.Add(nextPostition);
}

Note that these answers are also fixing a bug in your code as well. To expose the bug and still avoid the error, you can call ToList() on the collection inside the for loop, like so:

foreach(Vector3 t in positionList.ToList())
{
    //if there is no posiiton in the list where one should be
    if (nextPosition != t)
    {
        //then create a position
        positionList.Add(nextPosition);
    }
}

The call to ToList() here actually creates a new copy of the list and iterates over that one, so there is no conflict when the original one is modified. However, you will notice that your existing code will add nextPosition to the list every time it encounters a non-match, which I don't think is your intended behavior.

Rufus L
  • 36,127
  • 5
  • 30
  • 43
  • Thank you Rufus! I'm looking into lists and the functionality in unity and there doesn't seem to be a List.Any function. Would you happen to know how I can access this? Is there a collection I need to reference? or is it exclusive to certain types of lists? – Chris Winning May 15 '17 at 03:31
  • Yeah, it's a Linq extension. You'd need this line at the top of your file: `using System.Linq;` – Rufus L May 15 '17 at 03:37
  • Amazing! Rufus I could kiss you :D – Chris Winning May 15 '17 at 03:46
  • @ChrisWinning Please note that this answer *does not* answer question as asked. You should consider to [edit] the question so this post at least can be considered an answer (i.e. by adding explanation of what you trying to achieve). Also note that "build/compile" is very different from "run and get exception". – Alexei Levenkov May 15 '17 at 04:21
  • 1
    @AlexeiLevenkov I modified the answer with some more detail to hopefully better address the question. – Rufus L May 15 '17 at 04:59