Why Can't We Assign a Foreach Iteration Variable, Whereas We Can Completely Modify It with an Accessor

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.

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.

Why isn't assign on a foreach iteration variable in Array.ForEach an error?

foreach(int n in arr)
{
n++;
}

This is a language construct, the compiler knows exactly what a foreach-loop is supposed to do and what nis. It can therefore prevent you from changing the iteration variable n.

Array.ForEach(arr, (n) => {
n++;
});

This is a regular function call passing in a lambda. It is perfectly valid to modify local variables in a function (or lambda), so changing n is okay. While the compiler could warn you that the increment has no effect as it's never been used afterwards, it's valid code, and just because the function is called ForEach and actually does something similar to the foreach-loop doesn't change the fact that this is a regular function and a regular lambda.

Why are assignment operators (=) invalid in a foreach loop?

Here's your code:

foreach (string item in sArray)
{
item = "Some assignment.\r\n";
}

Here's a rough approximation of what the compiler does with this:

using (var enumerator = sArray.GetEnumerator())
{
string item;
while (enumerator.MoveNext())
{
item = enumerator.Current;

// Your code gets put here
}
}

The IEnumerator<T>.Current property is read-only, but that's not actually relevant here, as you are attempting to assign the local item variable to a new value. The compile-time check preventing you from doing so is in place basically to protect you from doing something that isn't going to work like you expect (i.e., changing a local variable and having no effect on the underlying collection/sequence).

If you want to modify the internals of an indexed collection such as a string[] while enumerating, the traditional way is to use a for loop instead of a foreach:

for (int i = 0; i < sArray.Length; ++i)
{
sArray[i] = "Some assignment.\r\n";
}

How to edit an iterator within a foreach loop

You can just store it and remove it form the collection afterwards.

var toRemove = null;
foreach (string item in hillracing.searchRaces(RaceID).RaceList) // Loop through List with foreach.
{
if (item == SelectedItem)
{
toRemove = item;
break; //Can break here if you're sure there's only one SelectedItem
}
}
hillracing.searchRaces(RaceID).Racelist.Remove(toRemove);

though in this case you could also just use hillracing.searchRaces(RaceID).Racelist.Remove(SelectedItem); and you won't use the foreach loop at all.

Foreach loop passing the iteration variable to a method with and without ref - dont get why it wont allow it with ref

Ultimately, the notes on CS1657 explains all of this; the l-value of a regular foreach is considered "readonly" - you can't do:

person = new Person();

for example. The ref usage requires the parameter not be read-only, otherwise we have violated the promise of read-only-less, because the method could modify something that is meant to be read-only. In this case, since Person is a class, this only relates to the actual reference itself, not the underlying object.

There is good news, though: we now have in, which is like ref, but for read-only scenarios, so: change public void ChangeName(ref Person person) to public void ChangeName(in Person person) and it should work:

myPeople.ChangeName(in person);

The restriction on this is that you then, in your ChangeName method, can't do things like:

public void ChangeName(in Person person)
{
person = new Person(); // invalid
}

Emphasis: this in usage isn't really intended for classes; it is intended for struct, and in particular readonly struct - to avoid defensive stack copies of complex and large value-types.

In latest versions of C#, there is also a ref readonly concept, by-which the l-value is a ref Foo (for whatever type Foo). This requires a custom iterator with a public ref Foo Current {get;} accessor instead of a public Foo Current {get;} accessor, i.e. it is explicitly tied into ref-return. When the l-value is a ref, you can use it in a method that takes a ref.


Important emphasis, although I realize OP knows this: you don't need ref in this scenario since Person is a class itself; the code would work just fine without the ref, and the object would be updated correctly.

Passing DataGridViewRow as referance

you cannot assign an iterated, enumerated item using a "foreach" or even a "for".
i got around that once, using that piece of code :

List<DataGridViewRow> lstDgr = new List<DataGridViewRow>();
foreach(DataGridViewRow dgr in dgvMarksEntryByClassWise.Rows)
{
DataGridViewRow dgrTemp = dgr;
RowValueSet(ref dgrTemp);
lstDgr.Add(dgrTemp);
}
dgvMarksEntryByClassWise.Rows.Clear();
dgvMarksEntryByClassWise.Rows.AddRange(lstDgr); //Not sure about the AddRange, try the Add method instead

Hope this helps

using foreach to store names of one array in another in c#

directory = new DirectoryInfo(camera_dir);    
string[] date = new string[directory.GetFiles().Length];
int i=0;
foreach (FileInfo file in directory.GetFiles())
{
name[i] = file.Name.Substring(11, 6);
i++;
}

Liststring emailAddress how to replace value in a particular string

I think you should try setting it back to itself email = email.Replace(";", ",");



Related Topics



Leave a reply



Submit