views:

231

answers:

4

I am getting this exception:

Collection was modified; enumeration operation may not execute.

when executing this section of code:

List<T> results = new List<T>();
foreach (T item in items)
    if (item.displayText.ToLower().Contains(searchText.ToLower()))
        results.Add(item);
return results;

I can't see why I get this exception as I'm not changing the contents of the items list.

A: 

The list might be modified by another thread. If that should be the case you have to use proper locking:

lock(items.SyncRoot)
{
    foreach (T item in items)
        ...
}

This locking would also have to be added to all other places where you modify the list.

Since you stated that your code is single-threaded, could you try if the following simple sample program works for you (it does for me)?

using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        var items = new List<Item>()
        { 
            new Item() { DisplayText = "A" },
            new Item() { DisplayText = "B" },
            new Item() { DisplayText = "AB" },
        };

        var res = filter(items, "A");
    }

    static List<T> filter<T>(List<T> items, string searchText) where T : Item
    {
        List<T> results = new List<T>();
        foreach (T item in items)
            if (item.DisplayText.ToLower().Contains(searchText.ToLower()))
                results.Add(item);
        return results;
    }
}

class Item
{
    public string DisplayText { get; set; }
}
0xA3
I'm not sure your code will do much to identify the problem. The two most likely culprits of the issue are the collection "items", which you have swapped out to be a list (and we have no idea what collection type it is in the OP's case), and the type T which you have swapped out for an anonymous type. Unless the OP is wrong and this is a simple threading issue, then the problem is almost certainly a side effect in the very two things you have swapped out.
Rob Levine
I added the sample only to demonstrate that the problem is actually not within the for-loop but in some other part of the code, which the OP didn't show (and I chose a List<T> for items because the OP said so in a comment, and T is of type `Item` and not anonymous).
0xA3
+3  A: 

With this we have to ask how is the 'items' enumerable produced? Is it a yield block? Use of yield blocks can suffer from the spectre of delayed execution (i.e. the enumerable not being prepared until the enumerator is accessed) and you can unexpected results from this.

Assuming that the Exception is being thrown from the items enumerator, then the only reason for it to be thrown is if the collection has been modified. Unless it's a custom class implementing its own enumerator, in which case there could simply be a bug in it.

You've discounted the multithreaded environment so I'll ignore that possibility.

Do you still get the error if you 'realise' the enumerable - i.e.

foreach(var item in items.ToArray())
{
   //your code.
}

I'll bet you don't, and if not, then something is definitely modifying your items list.

You could check that items and the result of the .ToArray() call above (you'd have to cache it to a local variable) are still the same at the end of your foreach loop.

Andras Zoltan
A: 

Try such code :)

string text = searchText.ToLower();
results = items
  .Where(item => item.displayText.ToLower().Contains(text))
  .ToList();
Alex Kofman
+1 for optimisation, but we still want to know why his/her code is throwing that exception :(
Codesleuth
visual studio won't let me do a .Where() on a list
harryovers
probably because you're missing a `using System.Linq`
Thomas Levesque
`using System.Linq` is there
harryovers
@harryovers: what is `items` then? If you have the `System.Linq` namespace defined, you should get all extension methods for `items` if it is IEnumerable, unless you're not using .NET 3.5?
Codesleuth
A: 

Generally I recommend not to ask such questions, but look into exception stack trace. Usually it contains exhaustive answer.

Update: My answer is not correct, you can declare items of ObservableCollection<T> type and subscribe on its CollectionChanged event to determine who exactly changes your collection (-:

Alex Kofman
The stack trace will almost certainly show the OP where the exception was raised - which we already know (iterating over the enumeration in the foreach). What the OP needs to find out is what is *modifying* the collection. Since *something* is doing it silently, looking at the stack trace of the exception is unlikely to help.
Rob Levine