Foreach statement in listbox - c#

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);
}
}

Related

C# Foreach Loop Doesn't Exit Properly [duplicate]

For now, the best I could think of is:
bool oneMoreTime = true;
while (oneMoreTime)
{
ItemType toDelete=null;
oneMoreTime=false;
foreach (ItemType item in collection)
{
if (ShouldBeDeleted(item))
{
toDelete=item;
break;
}
}
if (toDelete!=null)
{
collection.Remove(toDelete);
oneMoreTime=true;
}
}
I know that I have at least one extra variable here, but I included it to improve the readability of the algorithm.
The "RemoveAll" method is best.
Another common technique is:
var itemsToBeDeleted = collection.Where(i=>ShouldBeDeleted(i)).ToList();
foreach(var itemToBeDeleted in itemsToBeDeleted)
collection.Remove(itemToBeDeleted);
Another common technique is to use a "for" loop, but make sure you go backwards:
for (int i = collection.Count - 1; i >= 0; --i)
if (ShouldBeDeleted(collection[i]))
collection.RemoveAt(i);
Another common technique is to add the items that are not being removed to a new collection:
var newCollection = new List<whatever>();
foreach(var item in collection.Where(i=>!ShouldBeDeleted(i))
newCollection.Add(item);
And now you have two collections. A technique I particularly like if you want to end up with two collections is to use immutable data structures. With an immutable data structure, "removing" an item does not change the data structure; it gives you back a new data structure (that re-uses bits from the old one, if possible) that does not have the item you removed. With immutable data structures you are not modifying the thing you're iterating over, so there's no problem:
var newCollection = oldCollection;
foreach(var item in oldCollection.Where(i=>ShouldBeDeleted(i))
newCollection = newCollection.Remove(item);
or
var newCollection = ImmutableCollection<whatever>.Empty;
foreach(var item in oldCollection.Where(i=>!ShouldBeDeleted(i))
newCollection = newCollection.Add(item);
And when you're done, you have two collections. The new one has the items removed, the old one is the same as it ever was.
Just as I finished typing I remembered that there is lambda-way to do it.
collection.RemoveAll(i=>ShouldBeDeleted(i));
Better way?
A forward variation on the backward for loop:
for (int i = 0; i < collection.Count; )
if (ShouldBeDeleted(collection[i]))
collection.RemoveAt(i)
else
i++;
You cannot delete from a collection inside a foreach loop (unless it is a very special collection having a special enumerator). The BCL collections will throw exceptions if the collection is modified while it is being enumerated.
You could use a for loop to delete individual elements and adjust the index accordingly. However, doing that can be error prone. Depending on the implementation of the underlying collection it may also be expensive to delete individual elements. For instance deleting the first element of a List<T> will copy all the remaning elements in the list.
The best solution is often to create a new collection based on the old:
var newCollection = collection.Where(item => !ShouldBeDeleted(item)).ToList();
Use ToList() or ToArray() to create the new collection or initialize your specific collection type from the IEnumerable returned by the Where() clause.
The lambda way is good. You could also use a regular for loop, you can iterate lists that a for loop uses within the loop itself, unlike a foreach loop.
for (int i = collection.Count-1; i >= 0; i--)
{
if(ShouldBeDeleted(collection[i])
collection.RemoveAt(i);
}
I am assuming that collection is an arraylist here, the code might be a bit different if you are using a different data structure.

How to edit an iterator within a foreach loop

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);

Modifying list inside foreach loop

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();

Strange behaviour between foreach and for loop

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.

How to remove elements from an array

Hi I'm working on some legacy code that goes something along the lines of
for(int i = results.Count-1; i >= 0; i--)
{
if(someCondition)
{
results.Remove(results[i]);
}
}
To me it seems like bad practice to be removing the elements while still iterating through the loop because you'll be modifying the indexes.
Is this a correct assumption?
Is there a better way of doing this? I would like to use LINQ but I'm in 2.0 Framework
The removal is actually ok since you are going downwards to zero, only the indexes that you already passed will be modified. This code actually would break for another reason: It starts with results.Count, but should start at results.Count -1 since array indexes start at 0.
for(int i = results.Count-1; i >= 0; i--)
{
if(someCondition)
{
results.RemoveAt(i);
}
}
Edit:
As was pointed out - you actually must be dealing with a List of some sort in your pseudo-code. In this case they are conceptually the same (since Lists use an Array internally) but if you use an array you have a Length property (instead of a Count property) and you can not add or remove items.
Using a list the solution above is certainly concise but might not be easy to understand for someone that has to maintain the code (i.e. especially iterating through the list backwards) - an alternative solution could be to first identify the items to remove, then in a second pass removing those items.
Just substitute MyType with the actual type you are dealing with:
List<MyType> removeItems = new List<MyType>();
foreach(MyType item in results)
{
if(someCondition)
{
removeItems.Add(item);
}
}
foreach (MyType item in removeItems)
results.Remove(item);
It doesn't seem like the Remove should work at all. The IList implementation should fail if we're dealing with a fixed-size array, see here.
That being said, if you're dealing with a resizable list (e.g. List<T>), why call Remove instead of RemoveAt? Since you're already navigating the indices in reverse, you don't need to "re-find" the item.
May I suggest a somewhat more functional alternative to your current code:
Instead of modifying the existing array one item at a time, you could derive a new one from it and then replace the whole array as an "atomic" operation once you're done:
The easy way (no LINQ, but very similar):
Predicate<T> filter = delegate(T item) { return !someCondition; };
results = Array.FindAll(results, filter);
// with LINQ, you'd have written: results = results.Where(filter);
where T is the type of the items in your results array.
A somewhat more explicit alternative:
var newResults = new List<T>();
foreach (T item in results)
{
if (!someCondition)
{
newResults.Add(item);
}
}
results = newResults.ToArray();
Usually you wouldn't remove elements as such, you would create a new array from the old without the unwanted elements.
If you do go the route of removing elements from an array/list your loop should count down rather than up. (as yours does)
a couple of options:
List<int> indexesToRemove = new List<int>();
for(int i = results.Count; i >= 0; i--)
{
if(someCondition)
{
//results.Remove(results[i]);
indexesToRemove.Add(i);
}
}
foreach(int i in indexesToRemove) {
results.Remove(results[i]);
}
or alternatively, you could make a copy of the existing list, and instead remove from the original list.
//temp is a copy of results
for(int i = temp.Count-1; i >= 0; i--)
{
if(someCondition)
{
results.Remove(results[i]);
}
}

Categories