Why Is Foreach Loop Read-Only in C#

Why is foreach loop Read-Only in C#

That is because foreach is meant to iterate over a container, making sure each item is visited exactly once, without changing the container, to avoid nasty side effects.

See: foreach in MSDN

If you meant why would changes to an element like an integer not affect a container of integers, well this is because the variable of iteration in this case would be a value type and is copied, e.g.:

// Warning: Does not compile
foreach (int i in ints)
{
++i; // Would not change the int in ints
}

Even if the variable of iteration was a reference type, whose operations returned a new object, you wouldn't be changing the original collection, you would just be reassigning to this variable most of the time:

// Warning: Does not compile
foreach (MyClass ob in objs)
{
ob=ob+ob; // Reassigning to local ob, not changing the one from the original
// collection of objs
}

The following example has the potential to actually modify the object in the original collection by calling a mutating method:

// Warning: Does not compile
foreach (MyClass ob in objs)
{
ob.ChangeMe(); // This could modify the object in the original collection
}

To avoid confusion with regard to value vs reference types and the scenarios mentioned above (along with some reasons related to optimization), MS chose to make the variable of iteration readonly.

How is the iteration variable readonly?

There is special-case code in the compiler which enforces the read-only constraint on the iteration variable in a foreach block. It does not correspond to any modifier which is exposed in the language, so you can't explicitly declare local variables as read-only outside of this particular syntax.

Conceptually, this constraint is applied before the expansion. That is, if there are any assignments to the iteration variable, the compiler generates an error. Otherwise the code is expanded. In the expanded code there is no particular constraints on v since it is just a regular local variable. Therefore the constraint does not exist in the IL either.

So why is there this special-case read-only constraint with the foreach-syntax? Only the language designers can answer that, but I would guess it is just to avoid confusion. If the iterator variable was assignable, you might think you were able to modify the actual collection that way, but nothing would actually happen, since the underlying enumerator is read-only.

Why is The Iteration Variable in a C# foreach statement read-only?

Lets start out with a silly but illustrative example:

Object o = 15;
o = "apples";

At no point do we get the impression that we just turned the number 15 into a string of apples. We know that o is simply a pointer. Now lets do this in iterator form.

int[] nums = { 15, 16, 17 };

foreach (Object o in nums) {
o = "apples";
}

Again, this really accomplishes nothing. Or at least it would accomplish nothing were it to compile. It certainly wouldn't insert our string into the int array -- that's not allowed, and we know that o is just a pointer anyway.

Let's 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);
}

Were this to compile, the Location in your example stars out referring to a value in Map, but then you change it to refer to a new Position (implicitly created by the addition operator). Functionally it's equivalent to this (which DOES compile):

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 prohibit you from re-assigning the pointer used for iteration? Clarity for one thing -- you don't want people assigning to it thinking they've changed your position within the loop. Ease of implementation for another: The variable might hide some internal logic indicating the state of the loop in progress.

But more importantly, there is no reason for you to want to assign to it. It represents the current element of the looping sequence. Assigning a value to it breaks the "Single Responsibility Principle" or Curly's Law if you follow Coding Horror. A variable should mean one thing only.

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

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.

Can a foreach iteration variable not change for ANY reason?

Whenever you encounter read only concepts in C#, it's almost always a shallow form of read only - you can't change which object the variable is referring to, but you can make changes to that object's own properties (or call methods on it, etc)

The same applies for readonly fields of a class - you can't change the reference but you can mutate the object.

Threadsafety and foreach with static readonly arrays

The operations in your method are "threadsafe" only based on your claim that Their items never change.; however, nothing in the given code guarantees that.

The term threadsafe is normally used to mean that the code in question guarantees that the data will not change, or that if it can change, the operation will still produce the correct results.

You would need to perform your own synchronization logic to make the code truly threadsafe.

=== Answer to your comments:

The return value of GetEnumerator() -- what you're calling iterator -- is itself safe for exclusive use by the calling thread, but that's not the issue here. It's the underlying collection (the array) which is not thread safe, because it can change.

Changing the foreach to a for loop doesn't make the code any more thread safe. You need to either synchronize access to the collection, or make it immutable.

In your case, I suggest the latter given that the data in your array is constant. To make it concrete for you, here is some conceptual code:

private static readonly IEnumerable<string> staticReadOnlyData = Array.AsReadOnly( new string[] { "someKey0", "someKey1", "someKey2", ... } );

public bool SomeThreadSharedCall(string toCheck)
{
// #1
foreach (string s in staticReadOnlyData)
{
if (s == toCheck)
return true;
}
return false;

// #2
return staticReadOnlyData.Contains(toCheck);

// or #3
return staticReadOnlyData.Any(s => string.Compare(toCheck, s, StringComparison.OrdinalIgnoreCase) == 0);
}

Cannot assign to item because it is a foreach iteration variable

According to Eric Lippert's answer, The iteration variable is read-only because it is an error to write to it.

Looks like there is some rule in the compiler that stops you from compiling code that attempts to modify the iteration variable, even though it's not marked as readonly behind the scenes (it can't anyway because it's a local var).

I wrote an article with all the questions/answers I came up with while learning about IEnumerable/foreach.

Foreach loop only reading the last item

You iterate over the loop and with each iteration you undo the iteration that proceeded it as it will hit the false of each if statement for that item.

A better structure would be set all Checked states to false initially. Then iterate over the list and only set the Checked state to true IF it occurs in the list but do not set it to false.

You can also do this which is simpler to read, you can use a HashSet to increase performance if the list can be large. (thanks @AlexeiLevenkov)

CBBGN.Checked = list.Contains("BGN");
CBLan.Checked = list.Contains("LAN");
CBFGN.Checked = list.Contains("FGN");
CBWNM.Checked = list.Contains("WNM");
CBGGN.Checked = list.Contains("GGN");

Original Fix

CBBGN.Checked = false;
CBLan.Checked = false;
CBFGN.Checked = false;
CBWNM.Checked = false;
CBGGN.Checked = false;

foreach (string k in list)
{
if (k == "BGN")
CBBGN.Checked = true;
if (k == "LAN")
CBLan.Checked = true;
if (k == "FGN")
CBFGN.Checked = true;
if (k == "WNM")
CBWNM.Checked = true;
if (k == "GGN")
CBGGN.Checked = true;
}

Foreach vs for loop in C#. Creation of new object is possible in for loop, but not possible in foreach loop

foreach iteration loops are known as 'read-only contexts.' You cannot assign to a variable in a read-only context.

For more info: http://msdn.microsoft.com/en-us/library/369xac69.aspx

Why can't we assign a foreach iteration variable, whereas we can completely modify it with an accessor?

foreach is a read only iterator that iterates dynamically classes that implement IEnumerable, each cycle in foreach will call the IEnumerable to get the next item, the item you have is a read only reference, you can not re-assign it, but simply calling item.Value is accessing it and assigning some value to a read/write attribute yet still the reference of item a read only reference.



Related Topics



Leave a reply



Submit