For loop for InvalidOperationException - c#

I went through many questions related to the scenario of enumerating through a collection using foreach which results in this exception:
collection was modified enumeration operation may not execute
exception
Most of the time I see people suggesting to use a for loop instead of foreach loop.
Is there any specific reason to use a for loop to fix the problem? Or is there any better way to fix this kind of scenario?

When you are working on collection used up in foreach loop, operation like adding new item or removing item should not be done, which modifies the collection.
But when we do it the following way it works well
List<string> list = new List<string>();
foreach (string str in list.ToList())
{
list.Remove(str);
}
But when you do this, please note that .ToList() will actually create a new list which makes it work well. Creating new lists every time may not be a good practice.
Be wise when you implement the above code, or to keep it simple, just use a for loop instead.

As I posted above in comments, you can use While for mutating collections. This is just to show concept that you can work with mutating collection without fear that item will not be found at specific pointer. But you will need to track items yourself.
// you will need some sort of indexer
int idx = 0;
while (coll.count > idx)
{
if (condition met)
{
coll.RemoveAt(idx);
// you just removed an item, don't up the index
}
else
{
// Do something with item, for example, or add one more item
idx += 1;
}
}
May be not as efficient, but this way you're Ok with mutation of your collection

This scenario will only happen if you have an active enumerator for the collection. The foreach loop uses such an enumerator. A for loop does not, you have to keep track of where in the collection you currently are, normally by using an index variable.
There is no general pattern to fix this. Personally, I changed my logic if I found my code was modifying the collection it was iterating over, but maybe your code is better suited to the for loop approach.

You are getting this exception because in the foreach loop you are trying to modify the collection you are enumerating. In the for loop you can modify the elements of the collection but you need to be very careful not to go into infinite loop situation.

This is pretty much by design. You cannot modify a collection being iterated inside a foreach as explained in this MSDN Documentation
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.

The problem when you want to remove items from a list you're iterating over is that the enumerator loses track of where you are in the list.
It's a bit like sawing a branch on which you are sitting. You shoudn't do it because it will have unecptected behaviour.
Now, how can this be solved?
Quite easily, you need make a copy of your list, iterate over your copy, and remove the items from your original list.
Sounds like a lot of code, but the only thing that you need to do is add ToList() in your foreach statement.
Example:
// This will throw an error, because you're modifying the original collection in your foreach statement.
var persons = new List<Persons>();
persons.Add(new Person());
persons.Add(new Person());
persons.Add(new Person());
foreach (var p in persons) {
persons.remove(p);
}
// This will NOT throw an error, because you're looping over a copy of the pesons list and remove items from the original one.
persons.Add(new Person());
persons.Add(new Person());
persons.Add(new Person());
foreach (var p in persons.ToList()) {
persons.remove(p);
}
Now, why does this work?
It's because ToList() creates a copy of your list in memory. So basiclly, your iterating over a copy of the collection right now.

for each loop doesn't guarantee the order of sequence.
In this case if you are updating the same collection within for each and try to use it again you will get the collection modified error.
for loop guarantees the order of execution of you sequence. You can visualize at what instance you need to update your data within your for loop.
Concerned about Performance and memory:
class Program
{
static void Main(string[] args)
{
string[] test = { "one", "two", "three" }; // your string
// make a new list of strings to contain all the strings + all the modifications
List<string> changetest = new List<string>();
// print out the initial array of strings
foreach (string s in test)
Console.WriteLine(s);
Console.WriteLine();
// now change the string if it starts with #
foreach (string s in test)
if (s.StartsWith("#"))
changetest.Add(s.Insert(0, "*")); // insert a *
else
changetest.Add(s); // else just add
// print out the modified list of strings
foreach (string s in changetest)
Console.WriteLine(s);
Console.ReadKey();
}
}
In the above example to update/modify the 'test' sequence you need to maintain one more collection to receive the update.Though scope of the List is local and elements are less you may encounter a situation where you will be dealing with huge/unknown collection.
for each inherently says you cant modify the collection.
If you want more info on performance visit following Link

Related

Update list while adding the new item in a list of string in foreach loop

Suppose we have a List<string> assetIds = GetAllAssetId(assetentities); called assetIds. Now suppose we have a list with 10 AssetIds in it. If you wanted to add the new AssetItems in a list of string and I need to update the list while doing the foreach loop. what would be the best way to do it?
Please have a look of the code here.
I am getting an Error :Collection was modified; enumeration operation may not execute. If I try to go for second time in foreach loop.
Yout change the collection in foreach loop so what you could do - make a copy of the collection in loop
foreach(var element in assetIds.ToList()) {
assetIds.Add(new Item());
)
collection that the foreach loop operates on is different that the one you add to
This is one way to get rid of the error. In your case I assume that there won't be any cases where the newly added item would be used twice.
Edit:
But again think if this is the solution that you want to go for. Maybe rewriting your code is better option

Creating a consuming foreach in C#

Is there a way to create a consuming foreach loop in C#?
basically loop through a Collection items and simultaneously consume them?
In plain English: Instead of just looping through the elements, remove the item from the collection, do stuff with it, then go to the next.
EDIT I neglected to mention that I am using a producing consuming pattern and this is why I wanted a consuming foreach. Most of the answers here are completely valid for the simple case I described before this edit, although what I wanted is described in my answer
Use a Stack(last-in-first-out) or Queue(first-in, first-out).
A nice example is this "recursive" queue which lists all the files in a directory.
As pointed out in the comments for your question, using a Stack or a Queue is the best option for a "process-then-remove" pattern. If for some reason you dont't want to use these specialized classes, you can do something like the following with a plain List:
var items = new List<Item>() {item1, item2, item3};
while(items.Count > 0)
{
ProcessItem(item[0]);
items.RemoveAt(0);
}
But what you cannot do is to modify the collection inside the foreach loop, as explained in StepUp's answer.
The collection used in foreach is immutable. This is made by design.
MSDN says:
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.
Update 0.0:
It is possible to use for loop to edit collection:
To add an item to collection, you can use for loop:
List<string> coll = new List<string>() { "1", "2", "3", "4", "5" };
for (int i = coll.Count-1; i>=0 ; i--)
{
/* You can assign value to an item:
coll[i] = "";
You can add an item to the collection:
coll.Add(i.ToString());*/
coll.RemoveAt(i);
//or coll.Remove("your string value to delete");
}
Not exactly a "consuming foreach" but probably what you are looking for is the TPL DataFlow.
See for example:https://leanpub.com/tpldataflowbyexample/read or MSDN: Create a DataFlow Pipeline or MSDN: Implement a Producer / Consumer Scenario
A very very basic example can look like this:
var actionBlock = new ActionBlock<int>(n => Console.WriteLine(n));
for (int i = 0; i < 10; i++) {
actionBlock.Post(i);
}
Console.WriteLine("Done");
You can also link multiple blocks to create a more complex consuming strategy with forks and so on. You can also defined varying Scheduler types to the steps.
For very easy scenarios this might be oversized, but TPL is a very good scaling solution with give you a lot of control on how the dataflow should look like.
As stated in the answer from StepUp the collection used in the foreach loop is immutable. But a similar thing could be performed with BlockingCollection and using GetConsumingEnumerable which consumes the Collection items while iterating through them. Given that my collection is part of concurrent produce consume scheme (that I neglected to mention in my question) this suits me more. In simpler cases though the other answers, using a stack or queue are much more appropriate, or simple loop that would loop from the end of the collection, down to the start and remove the item on the way (a start to end loop with removal will skip items or end up in an IndexOutOfBounds Exception

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

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.

Enumerator problem, Any way to avoid two loops?

I have a third party api, which has a class that returns an enumerator for different items in the class.
I need to remove an item in that enumerator, so I cannot use "for each". Only option I can think of is to get the count by iterating over the enum and then run a normal for loop to remove the items.
Anyone know of a way to avoid the two loops?
Thanks
[update] sorry for the confusion but Andrey below in comments is right.
Here is some pseudo code out of my head that won't work and for which I am looking a solution which won't involve two loops but I guess it's not possible:
for each (myProperty in MyProperty)
{
if (checking some criteria here)
MyProperty.Remove(myProperty)
}
MyProperty is the third party class that implements the enumerator and the remove method.
Common pattern is to do something like this:
List<Item> forDeletion = new List<Item>();
foreach (Item i in somelist)
if (condition for deletion) forDeletion.Add(i);
foreach (Item i in forDeletion)
somelist.Remove(i); //or how do you delete items
Loop through it once and create a second array which contains the items which should not be deleted.
If you know it's a collection, you can go with reverted for:
for (int i = items.Count - 1; i >= 0; i--)
{
items.RemoveAt(i);
}
Otherwise, you'll have to do two loops.
You can create something like this:
public IEnumerable<item> GetMyList()
{
foreach (var x in thirdParty )
{
if (x == ignore)
continue;
yield return x;
}
}
I need to remove an item in that enumerator
As long as this is a single item that's not a problem. The rule is that you cannot continue to iterate after modifying the collection. Thus:
foreach (var item in collection) {
if (item.Equals(toRemove) {
collection.Remove(toRemove);
break; // <== stop iterating!!
}
}
It is not possible to remove an item from an Enumerator. What you can do is to copy or filter(or both) the content of the whole enumeration sequence.
You can achieve this by using linq and do smth like this:
YourEnumerationReturningFunction().Where(item => yourRemovalCriteria);
Can you elaborate on the API and the API calls you are using?
If you receive an IEnumerator<T> or IEnumerable<T> you cannot remove any item from the sequence behind the enumerator because there is no method to do so. And you should of course not rely on down casting an received object because the implementation may change. (Actually a well designed API should not expose mutable objects holding internal state at all.)
If you receive IList<T> or something similar you can just use a normal for loop from back to front and remove the items as needed because there is no iterator which state could be corrupted. (Here the rule about exposing mutable state should apply again - modifying the returned collection should not change any state.)
IEnumerator.Count() will decide at run-time what it needs to do - enumerate to count or reflect to see it's a collection and call .Count that way.
I like SJoerd's suggestion but I worry about how many items we may be talking about.
Why not something like ..
// you don't want 2 and 3
IEnumerable<int> fromAPI = Enumerable.Range(0, 10);
IEnumerable<int> result = fromAPI.Except(new[] { 2, 3 });
A clean, readable way to do this is as follows (I'm guessing at the third-party container's API here since you haven't specified it.)
foreach(var delItem in ThirdPartyContainer.Items
.Where(item=>ShouldIDeleteThis(item))
//or: .Where(ShouldIDeleteThis)
.ToArray()) {
ThirdPartyContainer.Remove(delItem);
}
The call to .ToArray() ensures that all items to be deleted have been greedily cached before the foreach iteration begins.
Behind the scenes this involves an array and an extra iteration over that, but that's generally very cheap, and the advantage of this method over the other answers to this question is that it works on plain enumerables and does not involve tricky mutable state issues that are hard to read and easy to get wrong.
By contrast, iterating in reverse, while not rocket science, is much more prone to off-by-one errors and harder to read; and it also relies on internals of the collection such as not changing order in between deletions (e.g. better not be a binary heap, say). Manually adding items that should be deleted to a temporary list is just unnecessary code - that's what .ToArray() will do just fine :-).
an enumerator always has a private field pointing to the real collection.
you can get it via reflection.modify it.
have fun.

Categories