tags:

views:

238

answers:

6

Why is a foreach loop is a read only loop? What reasons are there for this?

+11  A: 

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.

Mark Byers
+1. It is similar to saying `Foo x = new Foo(); Foo y = x; y = new Foo();` Setting `y` to `new Foo()` does nothing to `x`. Similarly, having `foo` as your loop variable and saying `foo = new Foo();` (if you could**) does nothing to the `Foo` in the collection.
Anthony Pegram
That's (the copy part) not true for reference type then x would hold a reference to the object in the IEnumerable
Rune FS
@Rune FS: In the case of reference types it's a *copy* of the reference. Even in this case it still probably wouldn't do what you wanted if you could assign to it.
Mark Byers
@Rune, `x` would contain the address of the object. Saying `x = new Bar()` would create a new `Bar` object and then store its address in `x`. The object in the collection would not be modified. It's no different than passing a parameter into a function and then modifying the parameter. The variable at the call site is unaffected (unless an `out` or `ref` modifier is used in the method signature).
Anthony Pegram
@mark I'm with you on not assigning to the 'x' but it makes a lot of differrence when you assign to a property/field of 'x' whether it's a value type or reference type. In the first case the change is lost when the loop ends in the second it last until changes again or the object is collected
Rune FS
@Anthony it seems you misunderstood me and it's a reference to the object that the variable holds this _might_ be implemented as a pointer but that not required by the specifications
Rune FS
@Rune FS: exactly, this is the idea that made me ask this question
George
+4  A: 

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.

taylonr
Microsoft's contract for iEnumerable says an iEnumerable should not provide any means of updating the collection without causing an exception to be thrown when enumerating the next element. If I had my druthers, an enumerator in your scenario would be free to include or not include Mississippi, but would be required to either enumerate exactly once all elements that existed throughout the enumeration, or throw an exception if it couldn't do that.
supercat
+2  A: 

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.

Rob
A: 

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.

jdehaan
A: 

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.

Guffa
A: 

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 at 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 view 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 you self because it no longer has anything to do with the enumerated list anymore.

(sry for not marking the code but the icon is not showing on my phone)

Rune FS