Why is the Iteration variable in a C # foreach statement read-only?

As I understand it, the C # foreach iteration variable is immutable.

This means that I cannot change the iterator as follows:

foreach (Position Location in Map) { //We want to fudge the position to hide the exact coordinates Location = Location + Random(); //Compiler Error Plot(Location); } 

I cannot directly change the iterator variable, and instead I have to use a for loop

 for (int i = 0; i < Map.Count; i++) { Position Location = Map[i]; Location = Location + Random(); Plot(Location); i = Location; } 

Based on the background of C ++, I see foreach as an alternative to the for loop. But with the above limitation, I usually refuse to use a for loop.

I am curious what is the basis for making an iterator immutable?




Edit:

This question is a matter of curiosity rather than a coding issue. I appreciated the coding responses, but cannot mark them as answers.

In addition, the above example was too simplified. Here is the C ++ example that I want to do:

 // The game rules: // - The "Laser Of Death (tm)" moves around the game board from the // start area (index 0) until the end area (index BoardSize) // - If the Laser hits a teleporter, destroy that teleporter on the // board and move the Laser to the square where the teleporter // points to // - If the Laser hits a player, deal 15 damage and stop the laser. for (int i = 0; i < BoardSize; i++) { if (GetItem(Board[i]) == Teleporter) { TeleportSquare = GetTeleportSquare(Board[i]); SetItem(Board[i], FreeSpace); i = TeleportSquare; } if (GetItem(Board[i]) == Player) { Player.Life -= 15; break; } } 

I cannot do the above in C # foreach because the iterator I am immutable. I think (correct me if I am wrong), this is typical for foreach design in languages.

I am wondering why the foreach iterator is immutable.

+32
c # language-design
Apr 22 '09 at 9:48
source share
6 answers

Let's start with stupid but illustrative examples:

 Object o = 15; o = "apples"; 

In no case does it seem that we just turned number 15 into a chain of apples. We know that o is just a pointer. Now let's do it in the form of an iterator.

 int[] nums = { 15, 16, 17 }; foreach (Object o in nums) { o = "apples"; } 

Again, this does nothing. Or, at least, he could not compile anything. This, of course, will not inject our string into the int array - it is not allowed, and we know that o is just a pointer.

Take your example:

 foreach (Position Location in Map) { //We want to fudge the position to hide the exact coordinates Location = Location + Random(); //Compiler Error Plot(Location); } 

If this is compiled, Location in your example will display with a reference to the value in Map , but then you will change it to refer to the new Position (implicitly created by the add statement). Functionally, it is equivalent to this (which compiles):

 foreach (Position Location in Map) { //We want to fudge the position to hide the exact coordinates Position Location2 = Location + Random(); //No more Error Plot(Location2); } 

So why does Microsoft forbid you to rewrite the pointer used for iteration? Clarity for one thing - you do not want people to appoint him, thinking that they have changed your position in the cycle. Ease of implementation for another: a variable can hide some internal logic indicating the state of the current loop.

But more importantly, there is no reason why you want to assign it. It represents the current element of the sequence of loops. Assigning a value to it violates the "principle of single responsibility" or Curly Law if you follow Coding Horror. A variable should mean only one thing.

+23
Apr 23 '09 at 3:22
source share

If the variable was volatile, this may lead to incorrect display. For example:

 string[] names = { "Jon", "Holly", "Tom", "Robin", "William" }; foreach (string name in names) { name = name + " Skeet"; } 

Some people might think that this will change the contents of the array. It comes a little, but it can be the reason. I'll watch it in my annotated performance tonight ...

+12
Apr 22 '09 at 9:55
source share

I think this is an artificial limitation, really do not need to do this. To prove this point, use this code as a consideration and a possible solution to your problem. The problem is asign, but internal changes to the objects do not present a problem:

 using System; using System.Collections.Generic; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { List<MyObject> colection =new List<MyObject>{ new MyObject{ id=1, Name="obj1" }, new MyObject{ id=2, Name="obj2"} }; foreach (MyObject b in colection) { // b += 3; //Doesn't work b.Add(3); //Works } } class MyObject { public static MyObject operator +(MyObject b1, int b2) { return new MyObject { id = b1.id + b2, Name = b1.Name }; } public void Add(int b2) { this.id += b2; } public string Name; public int id; } } } 

I did not know about these phenomena, because I always changed objects as I described.

+10
Apr 22 '09 at 11:35
source share

The foreach statement works with Enumerable. The thing that differs from Enumerable is that she does not know about the collection as a whole, she is almost blind.

This means that he does not know what will happen next, or really, if there is anything before the next iteration cycle. This is why you often see .ToList () so people can grab an Enumerable account.

For some reason, I'm not sure, this means that you need to make sure your collection does not change when you try to move the counter.

+2
Apr 22 '09 at 9:58
source share

When you modify a collection, the modification can have unpredictable side effects. The enumerator has no way of knowing how to deal with these side effects correctly, so they made collections unchanged.

See also this question: What is the best way to change the list in foreach .

+2
Apr 22 '09 at 11:00 a.m.
source share

The condition for working with IEnumerable objects is that the base collection should not change when you access it with Enumerable. You can assume that the Enumerable is a snapshot of the original collection. Therefore, if you try to change the collection on an enumeration, it will throw an exception. However, the extracted objects in Enumeration are not immutable at all.

Since the variable used in the foreach loop is local to the loop block, this variable, however, is not available outside the block.

+1
Apr 22 '09 at 11:14
source share



All Articles