My windows phone 7 silverlight app before placing pushpins on a map layer removes any that are previously there.
I was doing this in a foreach loop as follows:
//Clear previous pins
try
{
foreach (UIElement p in PushPinLayer.Children)
{
if(p.GetType() == typeof(Pushpin))
{
PushPinLayer.Children.Remove(p);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
//TODO: For some reason the foreach loop above causes an invalid Operation exception.
//Cathing the error here until I can work out why it is happening.
}
This code removes any pushpins as required but after the last loop would throw an exception "Invalid Operation" I re-wrote it as a for loop:
for (int i = 0; i < PushPinLayer.Children.Count; i++)
{
if (PushPinLayer.Children[i].GetType() == typeof(Pushpin))
{
PushPinLayer.Children.RemoveAt(i);
}
}
Which works fine, however I can't see why the foreach is throwing an error.
This is very normal,
You cannot remove items from a list which you still use in the foreach list.
better then removing the item would be to create a new list, and every time it is not a pushpin type, add the object to the new list.
This way the original list is not altered and you won't get an exception.
I find it strange that the for loop works, but if it does, it would mean that the way they are itterated is different. The for loop will be copied to another memory location and used for the for loop so that the original one, in which you remove items, is not used anymore by the for loop. The foreach loop will get parameters from the list, the you remove items, so the list and the parameters become concurrent.
Your foreach loop uses and Enumerator to iterate the objects in your collection. When you delete an object from the collection the Enumerator is no longer valid as it references object that no longer exist. This causes a InvalidOperationException
The best way to remove them is using a for loop and even better doing it in reverse.
for (int i = PushPinLayer.Children.Count - 1; i >= 0 ; i--)
{
if (PushPinLayer.Children[i].GetType() == typeof(Pushpin))
{
PushPinLayer.Children.RemoveAt(i);
}
}
This will ensure that as the items are removed your Index i does not exceed the number of items in your collection.
Since others have already answered your question, I will only comment on your usage of xxx.GetType() == typeof(Pushpin). You could check whether something is a pushpin by simply using the C# reserved keyword is. e.g.:
if (p is Pushpin) {...}
When you change the content of the collection, the enumerator used in the foreach loop becomes invalid. You can't change a collection while you enumerate it.
Here's a workaround:
List<UIElement> toRemove = new List<UIElement>();
foreach (UIElement p in PushPinLayer.Children)
{
if(p.GetType() == typeof(Pushpin))
{
toRemove.Add(p);
}
}
foreach(UIElement p in toRemove)
{
PushPinLayer.Children.Remove(p);
}
Alternatively, you could use the RemoveAll method, which takes a predicate as a parameter:
PushPinLayer.Children.RemoveAll(p => p is Pushpin);
Others have already given you the reason for the problem so I just thought I'd post a LINQ version of a work around
var toRemove = PushPinLayer.Children.OfType<Pushpin>().ToList();
// since toRemove is a separate collection, it's safe to do this now:
foreach (var child in toRemove)
PushPinLayer.Children.Remove(child)
You must not change the collection while foreaching through it.
Related
Scenario
I have a system that holds races, each race has a unique list of members on that race. (the list is a List< T > )
I want users to be able to remove a member (if they ARE this member) from the list of members on that race.
Problem
I'm trying to get the following code to work:
foreach (string item in hillracing.searchRaces(RaceID).RaceList) // Loop through List with foreach.
{
if (item == SelectedItem)
{
item = null;
}
}
I can't edit the variable because it is in a foreach loop, how would I achieve this another way?
You can just store it and remove it form the collection afterwards.
var toRemove = null;
foreach (string item in hillracing.searchRaces(RaceID).RaceList) // Loop through List with foreach.
{
if (item == SelectedItem)
{
toRemove = item;
break; //Can break here if you're sure there's only one SelectedItem
}
}
hillracing.searchRaces(RaceID).Racelist.Remove(toRemove);
though in this case you could also just use hillracing.searchRaces(RaceID).Racelist.Remove(SelectedItem); and you won't use the foreach loop at all.
You can't modify collection that you are looping using foreach loop. The collection used in foreach is immutable. This is by design.
The foreach statement is used to iterate through the collection to get
the information that you want, but can not be used to add or remove
items from the source collection to avoid unpredictable side effects.
If you need to add or remove items from the source collection, use a
for loop.
Using Linq you shouldn't need to loop to find the entry you want to nullify...
// Use of Single() here assumes the object definitely exists.
// Use SingleOrDefaul() if there is a chance it might not exist.
var item = hillracing.searchRaces(RaceID)
.RaceList
.Where(x => x.Item == SelectedItem).Single();
item = null;
Edit: Since you've changed the requirement to remove the item from the list, I think you'd just call the Remove method`with the found item. So the code becomes
// Use of Single() here assumes the object definitely exists.
// Use SingleOrDefaul() if there is a chance it might not exist.
var item = hillracing.searchRaces(RaceID)
.RaceList
.Where(x => x.Item == SelectedItem).Single();
hillracing.searchRaces(RaceID).RaceList.Remove(item);
You can't do that in a foreach loop. If it's an IList/IList<T> which allows random access, like an array or list, you can use a for-loop:
List<string> = hillracing.searchRaces(RaceID).RaceList;
for(int i = 0; i < list.Count; i++)
{
if(list[i] == SelectedItem)
list[i] = null;
}
So you can't add or remove items in the foreach but you also can't replace the reference. The object refers to the original value so you could modify the object(if strings werent immutable) but you can't replace the reference itself in a foreach. This is related.
Use the existing Remove()-method to search and remove the item for you:
hillracing.searchRaces(RaceID).RaceList.Remove(SelectedItem);
I am trying to remove object while I am iterating through Collection. But I am getting exception. How can I achieve this?
Here is my code :
foreach (var gem in gems)
{
gem.Value.Update(gameTime);
if (gem.Value.BoundingCircle.Intersects(Player.BoundingRectangle))
{
gems.Remove(gem.Key); // I can't do this here, then How can I do?
OnGemCollected(gem.Value, Player);
}
}
foreach is designed for iterating over a collection without modifing it.
To remove items from a collection while iterating over it use a for loop from the end to the start of it.
for(int i = gems.Count - 1; i >=0 ; i--)
{
gems[i].Value.Update(gameTime);
if (gems[i].Value.BoundingCircle.Intersects(Player.BoundingRectangle))
{
Gem gem = gems[i];
gems.RemoveAt(i); // Assuming it's a List<Gem>
OnGemCollected(gem.Value, Player);
}
}
If it's a dictionary<string, Gem> for example, you could iterate like this:
foreach(string s in gems.Keys.ToList())
{
if(gems[s].BoundingCircle.Intersects(Player.BoundingRectangle))
{
gems.Remove(s);
}
}
The easiest way is to do what #IV4 suggested:
foreach (var gem in gems.ToList())
The ToList() will convert the Dictionary to a list of KeyValuePair, so it will work fine.
The only time you wouldn't want to do it that way is if you have a big dictionary from which you are only removing relatively few items and you want to reduce memory use.
Only in that case would you want to use one of the following approaches:
Make a list of the keys as you find them, then have a separate loop to remove the items:
List<KeyType> keysToRemove = new List<KeyType>();
foreach (var gem in gems)
{
gem.Value.Update(gameTime);
if (gem.Value.BoundingCircle.Intersects(Player.BoundingRectangle))
{
OnGemCollected(gem.Value, Player);
keysToRemove.Add(gem.Key);
}
}
foreach (var key in keysToRemove)
gems.Remove(key);
(Where KeyType is the type of key you're using. Substitute the correct type!)
Alternatively, if it is important that the gem is removed before calling OnGemCollected(), then (with key type TKey and value type TValue) do it like this:
var itemsToRemove = new List<KeyValuePair<TKey, TValue>>();
foreach (var gem in gems)
{
gem.Value.Update(gameTime);
if (gem.Value.BoundingCircle.Intersects(Player.BoundingRectangle))
itemsToRemove.Add(gem);
}
foreach (var item in itemsToRemove)
{
gems.Remove(item.Key);
OnGemCollected(item.Value, Player);
}
As the other answers say, a foreach is designed purely for iterating over a collection without modifying it as per the documenation:
The foreach statement is used to iterate through the collection to get
the desired information, but should not be used to change the contents
of the collection to avoid unpredictable side effects.
in order to do this you would need to use a for loop (storing the items of the collection you need to remove) and remove them from the collection afterwards.
However if you are using a List<T> you could do this:
lines.RemoveAll(line => line.FullfilsCertainConditions());
After going through all the answers, and being equally good. I faced a challenge where I had to modify a List and what I ended up doing worked quite well for me. So just in case anyone finds it useful. Can someone provide me feedback on how efficient it might be.
Action removeFromList;
foreach(var value in listOfValues){
if(whatever condition to remove is){
removeFromList+=()=>listOfValues.remove(value);
}
}
removeFromList?.Invoke();
removeFromList = null;
You should use the for loop instead of the foreach loop. Please refer here
Collections support foreach statement using Enumarator. Enumerators can be used to read the data in the collection, but they cannot be used to modify the underlying collection. If changes are made to the collection, such as adding, modifying, or deleting elements, the enumerator is irrecoverably invalidated and the next call to MoveNext or Reset throws an InvalidOperationException.
Use for loop for collection modifying.
Look at this csharp code, and see if you can tell why I need to exit the loop after I found and deleted an item from the list. The idea is to go through a node's list of neighbors, and see if a Node n exists there, then delete that neighbor:
internal void RemoveDirected(Node n)
{
foreach (EdgeToNeighbor etn in this.Neighbors)
{
if (etn.Neighbor.Key == n.Key)
{
RemoveDirected(etn);
break;
}
}
}
internal void RemoveDirected(EdgeToNeighbor e)
{
Neighbors.Remove(e);
}
.
.
.
// Removes EdgeToNeighbor instance from AdjacencyList
protected internal virtual void Remove(EdgeToNeighbor e)
{
base.InnerList.Remove(e);
}
Notice how I have a "break" after the RemoveDirected call in the first method.
I've found that if I didn't exit after the RemoveDirected
it would go on forever in the foreach loop. I suppose it must have
something to do with the way foreach works. If you modify the list
that foreach is working on, it gets confuse and loops forever.
Have you seen this type of thing, and what are other options to use rather than using break?
Of course, I could place the node that I've found in a local variable, then break from the loop, and
delete it outside of the loop. But I was thinking, may be there are better ways to do this
in csharp.
When you iterate a .NET collection using its iterator, you must not modify that collection. If you do, you are asking for trouble.
You should defer the deletion instead of deleting right in the foreach loop. For example, you can collect everything you need to delete in a list, and then delete it outside of foreach.
var toDelete = this.Neighbors.Where(etn => etn.Neighbor.Key == n.Key).ToList();
foreach (var e in toDelete) {
Neighbors.Remove(e);
}
You definitely don't want to use an iterator if you are removing items. One option is to change it to downward counting loop:
for (int nI = this.Neighbors.Count; nI >= 0; nI--)
{
var etn = this.Neighbors[nI];
if (etn.Neighbor.Key == n.Key)
{
RemoveDirected(n);
}
}
You could also collect the keys or items to be deleted in a collection within the loop and then perform the deletion once you have completed your iteration.
However, if you are only removing a single item and the items in the collection are keyed somehow, then you shouldn't actually need any loop. Just test the existence of the key or item in the collection and, if it is there, just remove it.
You could get the number of nodes and then check and delete from highest to lowest in a for loop that way you avoid looking for an item that no longer exists.
I have a construction similar to this (but a lot more complicated):
var list = new List<string>();
// .. populate list ..
foreach(var item in list)
{
DoFunction(list);
}
public void DoFunction(List<string> list)
{
if(someCondition == true)
{
// .. modify list in here ..
}
}
Now, I understand that its not possible to edit the collection you're foreaching through, but how do you jump out of the loop gracefully if you do have to edit the list (without a try catch statement)? Is there some way to tell if the list has been edited? Can you edit the list and quickly break; before it notices?
Yes, you could break, if that's what you really want. An exception won't be thrown until the for loop tries to grab the next item from the list.
But I've found it's easiest just to create and iterate across a copy of the list so you don't have to worry about it.
foreach(var item in list.ToList())
The added performance overhead of an extra, untouched list is generally negligible compared to the maintainability costs of more complex code.
Rather than use a foreach construct, a for loop would allow you to alter the list.
for (var x = 0; x < list.Count; x++) {
}
It's hard to offer useful advice without knowing what kinds of edits are being made. The pattern that I've found is has the most general-purpose value, though, to just construct a new list.
For example, if you need to look at each item and decide between removing it, leaving it as-is, or inserting items after it, you could use a pattern like this:
IEnumerable<string> butcherTheList(IEnumerable<string> input)
{
foreach (string current in input)
{
if(case1(current))
{
yield return current;
}
else if(case2(current))
{
yield return current;
yield return someFunc(current);
}
// default behavior is to yield nothing, effectively removing the item
}
}
List<string> newList = butcherTheList(input).ToList();
I have a problem with a foreach statement in my project. So far I have the code:
foreach(object i in listboxFiles.Items)
{
if (i == ".ftpquota")
{
listboxFiles.Items.Remove(i);
}
if (i == ".")
{
listboxFiles.Items.Remove(i);
}
if (i == "..")
{
listboxFiles.Items.Remove(i);
}
}
I have this in a 1 second timer. It gets the item name all right, but when it gets to the if statements it says that they do not match, but they do?
First thing, you are changing a collection while iterating over it. This cannot work, so your code is fundamentally broken.
There are several ways to fix this; the simplest in your case would be to copy the items collection, iterating over the copy and changing (= removing from) the original:
var items = new System.Collections.ArrayList(listboxFiles.Items);
foreach (var item in items) {
if (item.Equals("."))
listboxFiles.Items.remove(item);
…
}
Secondly, you are comparing an object to a string, hence the == operator does reference equality checking rather than testing for string equality. Either use Equals or do an appropriate cast.
The equality check is not working because you should cast to string first and do an appropriate string comparison.
e.g.
if (string.Equals((string)i, ".ftpquota", StringComparison.Ordinal))
If you remove items from a collection of items whilst iterating through the collection, you may well run into trouble. One way to get around this problem is to start with the last item and count backwards, thus any removals you do will not affect the remaining items of the collection, e.g.
for(var i = listboxFiles.Items.Count - 1; i >= 0; --i)
{
var item = listboxFiles[i];
if (...)
{
listboxFiles.Items.RemoveAt(i);
}
}