George
George

Reputation: 298

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?

Upvotes: 10

Views: 16400

Answers (6)

Rune FS
Rune FS

Reputation: 21742

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.

Upvotes: 1

Guffa
Guffa

Reputation: 700342

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.

Upvotes: 1

jdehaan
jdehaan

Reputation: 19928

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.

Upvotes: 0

Rob
Rob

Reputation: 45771

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.

Upvotes: 2

taylonr
taylonr

Reputation: 10790

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.

Upvotes: 4

Mark Byers
Mark Byers

Reputation: 838216

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.

Upvotes: 21

Related Questions