1

For sake of simplicity; imagine a loop of classes car; where each car contain a list of car systems; the second loop goes through each system to remove them from that car. Ideally, at the end, each car will have no systems.

this is the simplified code.

public class ParseCars: MonoBehaviour 
{
    foreach (cars a_car in cars_list)
    {
        // retrieve the systems for the current car
        List<car_system> systems_list = a_car.ReturnSystemList();

        foreach (car_system a_system in systems_list)
        {
            a_car.RemoveSystem(a_system);
        }
        Debug.Log("removed all systems for this car);
    }
}

when removing the system, all that I do is to call the Remove method of the list, contained in the cars class.

The first removal is successful; then when it is time to remove the next element, instead of executing again the remove call, it goes back on the previous foreach, loading the next car in the list.

Looking at the name of the next system; I still see the previous, like if the loop would not take the next item in the list. I do not see any exception, it is the first time that I see this behavior, and can't really figure out what is happening, since I get no feedback out of the debugger.

  • Obviously this is not the an exact pseudocode of your algorithm.As @Saeid Yazdani suggests you probably modify your collection (although you should get an error) or doing something else wrong.If you switch to for you won't get a "CollecionModifiedException" but the code will not work without modifing the index.I suggest you use the List.RemoveAll method to empty a list or create a temp list with the elements to remove and the use RemoveRange on the original – George Vovos May 28 '16 at 09:42
  • 1
    It is simplified; otherwise I should add at least 3 more classes and methods. Saeid was spot on in seeing the issue. –  May 28 '16 at 09:53
  • 1
    Hey @newbiez what up. Don't forget in programming as a general principal, you often have to ***run through a list BACKWARDS*** when you are deleting. Saeid has given you the answer here. But don't forget that "trick", it's a basic in software. – Fattie May 28 '16 at 12:05
  • Hey @JoeBlow; I see. I thought that it won't matter the direction of deletion, since the C# list is not a sequential list like a linked list, nor a stack or heap. How come this old "trick" is still in place even in this case? –  May 28 '16 at 18:19
  • @nbew - I don't know :O I'm just reminding, that, sometimes you have to use that "trick" ! – Fattie May 28 '16 at 18:33
  • Found the reason :) Basically going forward with the for loop, result in shifting of elements that throw the counter off. This won't happen doing a foreach; but then we get the issue of non-modifiable copies created, so it won't work. Fascinating how this is not an issue in languages like Python for example (if you use foreach; there it is working fine) Once again you write something that is really useful; thanks! http://stackoverflow.com/questions/1582285/how-to-remove-elements-from-a-generic-list-while-iterating-over-it –  May 29 '16 at 01:53

1 Answers1

3

You should use a for loop. In foreach you can not alter the collection under the loop.

AFAIK, foreach uses an enumerator and Iteration variable in foreach is readonly. The enumerator returns a COPY of the object...thats why you do not get exception. You actually edited the copy of the object instead of the object itself.

Dumbo
  • 13,555
  • 54
  • 184
  • 288
  • That's why I could not see the error, THANKS!!!! Why VS does not throw an exception when trying to modify an unmodifiable object then? –  May 28 '16 at 09:37
  • 2
    AFAIK, foreach uses an enumerator and Iteration variable in foreach is readonly. The enumerator returns a COPY of the object...thats why you do not get exception. You actually edited the copy of the object instead of the object itself. – Dumbo May 28 '16 at 09:53
  • Makes sense; logic wise, the operation is legal, so the compiler is happy. Thanks. –  May 28 '16 at 09:54