Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

using Linq to generate a collection of things to be removed from another collection

I'm familiar with the problem of modifying a collection while looping over it with a foreach loop (i.e. "System.InvalidOperationException: Collection was modified"). However, it doesn't make sense to me that when I use Linq to create a List of keys to delete from a dictionary, then loop over my new List, I get the same exception.

Code before, that threw an exception:

IEnumerable<Guid> keysToDelete = _outConnections.Where(
    pair => pair.Value < timeoutPoint
).Select(pair => pair.Key);

foreach (Guid key in keysToDelete)
{
    ...some stuff not dealing with keysToDelete...
    _outConnections.Remove(key);
}

Code after, that worked:

List<Guid> keysToDelete = _outConnections.Where(
    pair => pair.Value < timeoutPoint
).Select(pair => pair.Key).ToList();

for (int i=keysToDelete.Count-1; i>=0; i--)
{
    Guid key = keysToDelete[i];
    ...some stuff not dealing with keysToDelete...
    _outConnections.Remove(key);
}

Why is this? I have the feeling that maybe my Linq queries aren't really returning a new collection, but rather some subset of the original collection, hence it accuses me of modifying the collection keysToDelete when I remove an element from _outConnections.

Update: the following fix also works, thanks to Adam Robinson:

List<Guid> keysToDelete = _outConnections.Where(
    pair => pair.Value < timeoutPoint
).Select(pair => pair.Key).ToList();

foreach (Guid key in keysToDelete)
{
    ...some stuff not dealing with keysToDelete...
    _outConnections.Remove(key);
}
like image 326
Sarah Vessels Avatar asked Jan 24 '23 03:01

Sarah Vessels


1 Answers

You're correct. LINQ uses what's called "deferred execution". Declaring your LINQ query doesn't actually do anything other than construct a query expression. It isn't until you actually enumerate over the list that the query is evaluated, and it uses the original list as the source.

However, calling ToList() should create a brand new list that has no relation to the original. Check the call stack of your exception to ensure that it is actually being thrown by keysToDelete.

like image 60
Adam Robinson Avatar answered Jan 25 '23 15:01

Adam Robinson