C# preventing Collection Was Modified exception

2019-02-21 16:52发布

问题:

Does

 foreach(T value in new List<T>(oldList) )

is dangerous (costly) when oldList contains 1 millions of object T ?

More generaly what is the best way to enumerate over oldList given that elements can be added/removed during the enumeration...

回答1:

The general rule is, you should not modify the same collection in which you are enumerating. If you want to do something like that, keep another collection which will keep track of which elements to add/remove from the original collection and then after exiting from the loop, perform the add/remove operation on the original collection.



回答2:

I usually just create a list for all the objects to be removed or added.

Within the foreach I just add the items to the appropriate collections and modify the original collection after the foreach have completed (loop through the removeItems and addItems collection)



回答3:

just like this

var itemsToBeRemoved = new List<T>();

foreach (T item in myHugeList) 
{
    if (/*<condition>*/)
         itemsToBeRemoved.Add(item);
}

myHugeList.RemoveRange(itemsToBeRemoved);


回答4:

If you mean you can add/remove objects from another thread, I would: 1-synchronize the threads 2- in the add/remove threads, create a list of items to be added or deleted 3- and then delete these items in a critical section (so it is small - you don't have to synch while adding the items to the delete list)

If you dont want to do that, you can use for instead of foreach, that would avoid the exception, but you would have to take extra care so you do not get other kinds of exceptions



回答5:

If you are using Foreach loop for modifying collection then you will get this error as below.

List<string> li = new List<string>();
    li.Add("bhanu");
    li.Add("test");

    foreach (string s in li)
    {
        li.Remove(s);
    }

Solution - use For Loop as below.

for (int i = 0; i < li.Count; i++)
    {
        li.RemoveAt(i);
        i--;
    }


回答6:

You could iterate through the list without using an enumerator, so do something like...

for(int i = 0;i<oldList.Count;i++) {
   var value = oldList[i];

   ...

   if(itemRemoveCondition) {
     oldList.RemoveAt(i--);
   }
}


回答7:

For me, first thing is you should consider using some kind of data paging, because having such 1-milion-items-large list could be dangerous itself.

Have you heard about Unit of Work pattern?

You can implement it so you mark objects for create, update or delete, and later, you call "SaveChanges", "Commit" or any other doing the job of "apply changes", and you'll get done.

For example, you iterate over the enumerable (oldList) and you mark them as "delete". Later, you call "SaveChanges" and the more abstract, generic unit of work will iterate over the small, filtered list of objects to work with.

  • http://martinfowler.com/eaaCatalog/unitOfWork.html

Anyway, avoid lists of a milion items. You should work with paged lists of objects.



回答8:

It will be 'slow' but there is not much more you can do about it, except running it on a background thread. E.g. using a BackgroundWorker.

If your operations on the list only occur on one thread, the correct approach is to add the items to add/remove to seperate lists, and perform those operations after your iterations has finished.

If you use multiple threads you will have to look into multithreaded programming, and e.g. use locks or probably better a ReaderWriterLock.

UPDATE: As mentioned in another Stack Overflow question, this is now possible without any effort in .NET 4.0 when using concurrent collections.



回答9:

foreach(T value in new List(oldList).ToList() ) - give a try



回答10:

you can use a flag to switch the modification to a temporary list while the original is being enumerated.

/// where you are enumerating

isBeingEnumerated = true
foreach(T value in new List<T>(oldList) )
isBeingEnumerated = false
SyncList(oldList with temporaryList)

/// where you are modifying while enumerating

if isBeingEnumerated then
use a temporaryList to make the changes.