tags:

views:

176

answers:

7
foreach(BruteforceEntry be in Entries.Values)
{
    if (be.AddedTimeRemove <= now)
        Entries.Remove(be.IPAddress);
    else if (be.Unbantime <= now && be.Unbantime.Day == DateTime.Now.Day)
        Entries.Remove(be.IPAddress);
}

An exception was thrown: Collection was modified; enumeration operation may not execute. For some reason, it is not anymore.

EDIT: I know you cannot remove something, while iterating through it this way, my question is hhow do I solve it?

+1  A: 

You can't modify the collection when iterating over it with foreach. Instead, iterate over it with a for loop.

David
If your going to be removing items from the collection, be sure to iterate over it in reverse (from length-1 to 0) and not 0 to length-1
Dave White
You better iterate backwards if you're removing items and update your increment variable accordingly if adding. This is prone to mistakes.
Marc
Iterating over a *dictionary* with a for loop is kinda tricky anyway...
Jon Skeet
True, it would make for some slightly uglier code. Just harkening back to my old Perl days, I suppose :)
David
+10  A: 

You can't modify a collection you're iterating over. In this case, a much better solution would be to create a list of entries to remove by iterating over the dictionary, and then iterate over that list, removing the entries from the dictionary:

List<string> removals = new List<string>();                    
DateTime now = DateTime.Now;
foreach(BruteforceEntry be in Entries.Values)
{
    if (be.AddedTimeRemove <= now ||
        (be.Unbantime <= now && be.Unbantime.Day == DateTime.Now.Day))
    {
        removals.Add(be.IPAddress);
    }
}
foreach (string address in removals)
{
    Entries.Remove(address);
}

Note that if you're using .NET 3.5, you could use a LINQ query to express the first part:

List<string> removals = (from be in Entries.Values
                         where be.AddedTimeRemove <= now ||
                               (be.Unbantime <= now && 
                                be.Unbantime.Day == DateTime.Now.Day)
                         select be.IPAddress).ToList();
Jon Skeet
Thanks a lot. I have 1 question, do you like this more than simly iterating through a copy, using .ToList() since that is a smaller edit.
Basser
@Basser: Well, for one thing this is likely to have a smaller memory footprint. It's also only iterating over the whole collection once - your current solution iterates once to build up the list, and then it iterates over that list. Don't get me wrong - it'll work... I just prefer this approach.
Jon Skeet
@Jon, well I just got told a top SO user answered my question, that's why I thought I'd ask you! I guess I'll use this method, as you are correct! Thanks again. I'm not too good with collections yet, still learning.
Basser
+1  A: 

You can change foreach(BruteforceEntry be in Entries.Values) to foreach(BruteforceEntry be in new List<BruteforceEntry>(Entries.Values))

That way you don't modify your collection, but rather a copy of it.

Albin Sunnanbo
Thank you, this would work if someone else didn't answer.
Basser
This won't work because you are still getting an iterator from the List<>. It's the iterator, not the collection, that is causing the problem.
Dave White
It sure works, you are not removing from the List<> iterator, you are removing from the Dictionary<> iterator. The List<> enumerates the complete Dictionary<> iterator before the first remove from the dictionary.
Albin Sunnanbo
I understand your intent now and you're correct.
Dave White
A: 

Boiling down your problem...

foreach(... ... in Entries.Values)
{
        Entries.Remove(...);

}

As the others have said you are modifying the iterator while iterating.

You can, as @David said, use a for loop instead, but make sure to start at the end (reverse iteration).

BioBuckyBall
+1  A: 

Simply you can't remove an entry while you iterate on it.

Just as an example of workaround you can iterate over a shallow copy of the collection:

foreach(BruteforceEntry be in Entries.Values.ToList())
{
 ....
}
digEmAll
Thank you, this solved my question.
Basser
This won't work because you are still getting an iterator from the List<>. It's the iterator, not the collection, that is causing the problem.
Dave White
My solution was only an example of workaround. In terms of performance, and clarity I suggest you to adopt Jon's solution ;)
digEmAll
@Dave, the iterator is created from the ToList List instance, the modification is against Entries. This works.
Marc
I understand your intent now and you're correct.
Dave White
+1  A: 

It looks like your question is about why an exception is not being thrown where it used to be. As the other answers state, you generally cannot change a collection through which you are iterating, but you are iterating over the Values collection, which, I believe, is a copy of the values in the dictionary, rather than referring to the main dictionary collection itself. So it no longer has the problem of iterating and modifying the same thing.

BlueMonkMN
A: 

This (in my opinion) is the easiest way:

Dictionary<String, String> A = new Dictionary<string, string>(); //Example Dictionary
A.Add("A", "A"); //Example Values
A.Add("B", "B");
A.Add("C", "C");

for (int i = A.Count - 1; i >= 0; i--) //Loop backwards so you can remove elements.
{
     KeyValuePair<String, String> KeyValue = A.ElementAt(i); //Get current Element.
     if (KeyValue.Value == "B") A.Remove(KeyValue.Key);
}

In your case:

for (int i = Entries.Count - 1; i >= 0; i--)
{
    KeyValuePair<String, BruteforceEntry> KeyValue = Entries.ElementAt(i);
    if (KeyValue.Value.AddedTimeRemove <= now)
         Entries.Remove(KeyValue.Key);
    else if (KeyValue.Value.Unbantime <= now && KeyValue.Value.Unbantime.Day == DateTime.Now.Day)
         Entries.Remove(KeyValue.Key);
}
Blam