Foreach while adding items to the looping collection - c#

This is the situation:
I'm browsing through some code and I wondered if the following statement takes a reference of the selected collection or a copy with which it replaces the original object when the foreach loop finishes. If the first, will it take the new found pages and join them in the loop?
foreach(Page page in Pages)
{
page.AddRange(RetrieveSubPages(page.Id));
}
Edit: I'm sorry, I made a typo.
It should be this:
foreach(Page page in pages)
{
pages.AddRange(RetrieveSubPages(page.Id));
}
What i tried to say is that if i add some objects to the enumerating collection, will it join those objects in the foreach?

It looks like the code doesn't modify the Pages collection, but the content of the objects in the Page objects in the Pages collection. The Page type having at least collection like method.
In general each collection implements iteration in a way suitable for itself, and generally becomes unmodifiable while iterating, but one could implelment a collection which iterates by taking a snapshot of itself.
There is no mechanism to detect exit from a loop which would allow action to be taken at that point (consider how this would interact with exceptions, break and return in the body of the loop).

In most cases, foreach works against the live collection (no explicit clone), and if you try to change the collection while enumerating it, then the enumerator breaks with an exception. So if you are adding to Pages, expect problems.

I think the safest way is this:
Array<Page> newpages = new Array<Page>();
foreach(Page page in pages)
{
newpages.AddRange(RetrieveSubPages(page.Id));
}
pages.AddRange(newpages);
You'd have to extend this a bit if you wanted to recurse into the subpages.

In response to you question, it does not make a copy.
It creates an enumerator and iterates through the collection. If the collection is changed while this enumeration is happening, in the foreach itself, or asynchronously, you will get an exception:
An unhandled exception of type 'System.InvalidOperationException' occurred in mscorlib.dll
Additional information: Collection was modified; enumeration operation may not execute.
You can, use a temporary collection and join the two afterwards, or just not use an enumerator.
for (int i = 0; i < pages.Count; i++)
{
test.AddRange(RetrieveSubPages(pages[i].Id));
}

foreach uses an enumerator.
The collection over which you loop using foreach, has to implement IEnumerable (or IEnumerable<T>).
Then, foreach calls the GetEnumerator method of that collection, and uses the Enumerator to traverse the collection.

You are not modifying the collection you are enumerating, therefore you won't have any problems with this code.
It is also irrelevant, if an clone of the collection is being enumerated, because the objects contained by both, collection and clone, are still the same (reference equals).

I'm pretty sure you'll get an exception thrown complaining that the underlying collection was modified

Related

does foreach loop handle Changes in list length correctly?

does foreach correctly iterate over flexible list?
for example
//will iterate over all items in list?
foreach (var obj in list)
{
//list length changes here
//ex:
list.Add(...);
list.Remove(...);
list.Concat(...);
// and so on
}
and if it does ...how?
You can't modify a collection while enumerating it inside a foreach statement.
You should use another pattern to do what you are trying to do because the for each does not allow you to change the enumerator you are looping to.
For Example:
Imagine if you run a foreach on a sorted list from the beginning, you start processing item with key="A" then you go to "B" then you change "C" to "B", what's going to happen? Your list is resorted and you don't know anymore what you are looping and where you are.
In general you "could" do it with a for(int i=dictionary.count-1; i>=0; --i) or something like that but this also depends on your context, I would really try to use another approach.
Internal Working: IEnumerator<t> is designed to enable the iterator pattern for iterating over collections of elements, rather than the length-index. IEnumerator<t> includes two members.
The first is bool MoveNext(). Using this method, we can move from one element within the collection to the next while at the same time detecting when we have enumerated through every item using the Boolean return.
The second member, a read-only property called Current, returns the element currently in process. With these two members on the collection class, it is possible to iterate over the collection simply using a while loop.
The MoveNext() method in this listing returns false when it moves past the end of the collection. This replaces the need to count elements while looping. (The last member on IEnumerator<t> , Reset(), will reset the enumeration.)
Per the documentation, if changes are made inside the loop the behavior is undefined. Undefined means that there are no restrictions on what it can do, there is no "incorrect behavior" when the behavior is undefined...crash, do what you want, send an email to your boss calling him nasty names and quiting, all equally valid. I would hope for a crash in this case, but again, whatever happens, happens and is considered "correct" according to the documentation.
You cannot change the collection inside the for each loop of the same collection.
if you want you can use for loop to change the collection length.
The collection you use in a foreach loop is immutable. As per MSDN
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.
But as per this link, it looks like this is now possible from .Net 4.0

In c# is it safe to expand a List that's being traversed with foreach?

In c# is it safe to expand a List that's being traversed with foreach?
I assume that by expand you mean to add new items to the collection. If so then the answer is not, you will get an exception on the traversal. I don't believe any collection can do this.
You can create a new list and then do an AddRange on the original list.
In c# is it safe to expand a List that's being traversed with foreach? If not then how about other collections?
There are very few collections that safely let you add to them while being iterated. There are quite a few options here - The most common would be to either build a new collection from the original, or add items into a temporary collection while iterating, then add them all to the original collection at the end.
The only collections in the framework which are designed with iteration and insertion in mind are some of the concurrent collections. For example, you can be iterating a BlockingCollection<T> via GetConsumingEnumerable and Add items to it at the same time. However, this is intended for a different purpose - it's typically used when having a separate consumer and producer thread, one adding, while the other processes items. As such, doing this within its own loop would be a very odd use case.
No, you will get an exception. While it isn't recommended, you can accomplish what you're looking for by using a simple for loop. The reason you're getting an exception is because of how foreach works. When compiled it is actually using the IEnumerable<T> or IEnumerable that is implemented by the List<T> to get the items. Now you can create your own collection which would allow something like this, but again, not recommended.
foreach is only for seeing values of any collection, if you'll be change number of elements in collection - exception will be thrown. If you will be change values in collection - nothing will happen, but Microsoft advice not to use foreach for such case.
IF you need to change elements or number of elements use list.ToArray() and FOR cycle through the array.
Simply create a new list. For example with buttons:
List<Button> list = new List<Button>();
list.Add(new Button());
list.Add(new Button());
foreach (Button button in new List<Button>(list))
list.Add(new Button());
Not the best solution, but probably the easiest.
Some possibly good, some complicated solutions here. How about cyling though the list with a while based on the lists length and the current position, that should work?
UInt16 n = 0;
while (n < list.Count)
{
... // might add new elements to the end of the list
n++;
}

Why am I getting an InvalidOperationException?

foreach (var shotItem in Invadershots)// it points to me to there and doesnt allow me to loop.."{"Collection was modified; enumeration operation may not execute."}"
{
shotItem.Move();// it happens when this simple method called (which actually checks some bool..if the shot was out of the winform).
if (shotItem.removeShot)
{
Invadershots.Remove(shotItem);
}
}
Could it be because i change the List items simultaneously?
How can i prevent that error from occurring?
This is because you trying modify collection Invadershots
Invadershots.Remove(shotItem);
This is not allowed within foreach, use for instead..
You cannot alter a collection whilst enumerating across it. Create a clone of the collection and alter that.
You can't do that deleting an element into a List, that you'r reading in a foreach will crash, surely, try to make a copy to remove with that while you're in the foreach, or make a for iteration and control de number of elements correctly and the out condition.
See you

C# Modification of IEnumerable while Enumerating with ForEach

This is something that I was exploring to see if I could take what was
List<MdiChild> openMdiChildren = new List<MdiChild>();
foreach(child in MdiManager.Pages)
{
openMdiChildren.Add(child);
}
foreach(child in openMdiChild)
{
child.Close();
}
and shorten it to not require 2 foreach loops.
Note I've changed what the objects are called to simplify this for this example (these come from 3rd party controls). But for information and understanding
MdiManager.Pages inherits form CollectionBase, which in turn inherits IEnumerable
and MdiChild.Close() removes the open child from the MdiManager.Pages Collection, thus altering the collection and causing the enumeration to throw an exception if the collection was modified during enumeration, e.g..
foreach(child in MdiManage.Pages)
{
child.Close();
}
I was able to the working double foreach to
((IEnumerable) MdiManager.Pages).Cast<MdiChild>.ToList()
.ForEach(new Action<MdiChild>(c => c.Close());
Why does this not have the same issues dealing with modifying the collection during enumeration? My best guess is that when Enumerating over the List created by the ToList call that it is actually executing the actions on the matching item in the MdiManager.Pages collection and not the generated List.
Edit
I want to make it clear that my question is how can I simplify this, I just wanted to understand why there weren't issues with modifying a collection when I performed it as I have it written currently.
Your call to ToList() is what saves you here, as it's essentially duplicating what you're doing above. ToList() actually creates a List<T> (a List<MdiChild> in this case) that contains all of the elements in MdiManager.Pages, then your subsequent call to ForEach operates on that list, not on MdiManager.Pages.
In the end, it's a matter of style preference. I'm not personally a fan of the ForEach function (I prefer the query composition functions like Where and ToList() for their simplicity and the fact that they aren't engineered to have side-effects upon the original source, whereas ForEach is not).
You could also do:
foreach(child in MdiManager.Pages.Cast<MdiChild>().ToList())
{
child.Close();
}
Fundamentally, all three approaches do exactly the same thing (they cache the contents of MdiManager.Pages into a List<MdiChild>, then iterate over that cached list and call Close() on each element.
When you call the ToList() method you're actually enumerating the MdiManager.Pages and creating a List<MdiChild> right there (so that's your foreach loop #1). Then when the ForEach() method executes it will enumerate the List<MdiChild> created previously and execute your action on each item (so that's foreach loop #2).
So essentially it's another way of accomplishing the same thing, just using LINQ.
You could also write it as:
foreach(var page in MdiManager.Pages.Cast<MdiChild>.ToList())
page.Close();
In any case, when you call ToList() extension method on an IEnumerable; you are creating a brand new list. Deleted from its source collection ( in this case, MdiManager.Pages ) will not affect the list output by ToList().
This same technique can be used to delete elements from a source collection without worrying about affecting the source enumerable.
You're mostly right.
ToList() creates a copy of the enumeration, and therefore you are enumerating the copy.
You could also do this, which is equivalent, and shows what you are doing:
var copy = new List<MdiChild>(MdiManager.Pages.Cast<MdiChild>());
foreach(var child in copy)
{
child.Close();
}
Since you are enumerating the elements of the copy enumeration, you don't have to worry about modifying the Pages collection, since each object referece that existed in the Pages collection now also exists in copy and changes to Pages don't affect it.
All the remaining methods on the call, ForEach() and the casts, are superfluous and can be eliminated.
At first glance, the culprit is ToList(), which is a method returning a copy of the items as a List, thus circumventing the problem.

Why can't I modify the loop variable in a foreach?

Why is a foreach loop a read only loop? What reasons are there for this?
I'm not sure exactly what you mean by a "readonly loop" but I'm guessing that you want to know why this doesn't compile:
int[] ints = { 1, 2, 3 };
foreach (int x in ints)
{
x = 4;
}
The above code will give the following compile error:
Cannot assign to 'x' because it is a 'foreach iteration variable'
Why is this disallowed? Trying to assigning to it probably wouldn't do what you want - it wouldn't modify the contents of the original collection. This is because the variable x is not a reference to the elements in the list - it is a copy. To avoid people writing buggy code, the compiler disallows this.
I would assume it's how the iterator travels through the list.
Say you have a sorted list:
Alaska
Nebraska
Ohio
In the middle of
foreach(var s in States)
{
}
You do a States.Add("Missouri")
How do you handle that? Do you then jump to Missouri even if you're already past that index.
If, by this, you mean:
Why shouldn't I modify the collection that's being foreach'd over?
There's no surety that the items that you're getting come out in a given order, and that adding an item, or removing an item won't cause the order of items in the collection to change, or even the Enumerator to become invalid.
Imagine if you ran the following code:
var items = GetListOfTOfSomething(); // Returns 10 items
int i = 0;
foreach(vat item in items)
{
i++;
if (i == 5)
{
items.Remove(item);
}
}
As soon as you hit the loop where i is 6 (i.e. after the item is removed) anything could happen. The Enumerator might have been invalidated due to you removing an item, everything might have "shuffled up by one" in the underlying collection causing an item to take the place of the removed one, meaning you "skip" one.
If you meant "why can't I change the value that is provided on each iteration" then, if the collection you're working with contains value types, any changes you make won't be preserved as it's a value you're working with, rather than a reference.
The foreach command uses the IEnumerable interface to loop throught the collection. The interface only defined methods for stepping through a collection and get the current item, there is no methods for updating the collection.
As the interface only defines the minimal methods required to read the collecton in one direction, the interface can be implemented by a wide range of collections.
As you only access a single item at a time, the entire collection doesn't have to exist at the same time. This is for example used by LINQ expressions, where it creates the result on the fly as you read it, instead of first creating the entire result and then let you loop through it.
Not sure what you mean with read-only but I'm guessing that understanding what the foreach loop is under the hood will help. It's syntactic sugar and could also be written something like this:
IEnumerator enumerator = list.GetEnumerator();
while(enumerator.MoveNext())
{
T element = enumerator.Current;
//body goes here
}
If you change the collection (list) it's getting hard to impossible to figure out how to process the iteration.
Assigning to element (in the foreach version) could be viewed as either trying to assign to enumerator.Current which is read only or trying to change the value of the local holding a ref to enumerator.Current in which case you might as well introduce a local yourself because it no longer has anything to do with the enumerated list anymore.
foreach works with everything implementing the IEnumerable interface. In order to avoid synchronization issues, the enumerable shall never be modified while iterating on it.
The problems arise if you add or remove items in another thread while iterating: depending on where you are you might miss an item or apply your code to an extra item. This is detected by the runtime (in some cases or all???) and throws an exception:
System.InvalidOperationException was unhandled
Message="Collection was modified; enumeration operation may not execute."
foreach tries to get next item on each iteration which can cause trouble if you are modifying it from another thread at the same time.

Categories