Can't modify members because it is a 'foreach iteration variable'

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

I am getting this error when I tried to modify one field inside foreach loop.

public struct myStruct
{
public int a;
public bool b;
//...
}

private List<myStruct> MyStruct = new List<myStruct>();
//...

foreach (myStruct ms in MyStruct)
{
if (ms.a == 3)
{
ms.b = true; // Can't modify members because... ?
}
}

So I tried below code that could be compiled anyway

foreach (myStruct ms in MyStruct)
{
if (ms.a == 3)
{
mtStruct copy = ms;
copy.b = true; // compiled successfully..
}
}

What would you do in this case?

Thanks
Bob
 
// Can't modify members because... ?
Essentially, because it is a struct
mtStruct copy = ms;
copy.b = true; // compiled successfully..
Note that this doesn't actually achieve anything; because of value-
type semantics, unless you push the updated struct back into the List
(over the top of the original), you have only updated the local copy;
the version in the list is unchanged.

To be honest, mutable structs (i.e .structs with settable properties)
are usually a design mistake, as they are massively prone to bugs of
this nature. Is there any reason that this isn't a class? Defining
structs in CLR is the exception, not the norm - most of the time you
tend to work with classes. And even then, most structs are (or should
be) immutable.
What would you do in this case?
Use a class ;-p Without more context, I can't tell if this is
reasonable...

Marc
 
Marc Gravell said:
To be honest, mutable structs (i.e .structs with settable properties)
are usually a design mistake, as they are massively prone to bugs of
this nature. Is there any reason that this isn't a class? Defining
structs in CLR is the exception, not the norm - most of the time you
tend to work with classes. And even then, most structs are (or should
be) immutable.

Even when working with API calls it's possible to use classes so the need
for structs is pretty rare.
 
bbg said:
I am getting this error when I tried to modify one field inside foreach
loop.

foreach (myStruct ms in MyStruct)
{
if (ms.a == 3)
{
ms.b = true; // Can't modify members because... ?
}
}

What would you do in this case?

Thanks
Bob


The 'foreach' language keyword is actually calling GetEnumerator() behind
the scenes. While using an enumerator, the collection cannot be changed,
( otherwise the enumerator would not enumerate correctly ). You can,
however, alter the contents with a regular 'for' loop. Because a struct is
a value type, you cannot alter it directly in the collection. Rather, you
must get a copy, and put it back into the collection.

for ( int i = 0; i < MyStruct.Count; i++ )
{
// use local copy for modification
myStruct ms = MyStruct;

// test condition
if ( ms.a == 3 )
{
// apply changes
ms.b = true;

// must assign back because of value type semanitcs
MyStruct = ms;
}
}
 
Just to clarify, if I pass MyStruct to other method, its content will be
copied because it's a value type. What if I pass List<MyStruct> to other
method and other method call Add()?
Bob
 
bbg said:
Just to clarify, if I pass MyStruct to other method, its content will be
copied because it's a value type. What if I pass List<MyStruct> to other
method and other method call Add()?

That will work fine. List is a reference type.

Michael
 
bbg said:
Just to clarify, if I pass MyStruct to other method, its content will be
copied because it's a value type. What if I pass List<MyStruct> to other
method and other method call Add()?

Calls Add() to do what?

A method calling Add() would in fact be able to modify the List<>
itself, by adding a new element to the List<>. But it still wouldn't be
changing the value of an existing element in the list.

Now, in the List<> class you can use the indexer to do this. So you can
do something like this:

void ModifyItem(List<MyStruct> mystructs, int imystruct)
{
MyStruct mystruct = mysructs[imystruct];

mystruct.b = true;
mystructs[imystruct] = mystruct;
}

So yes, if you are passing the entire List<> itself, you can in fact
modify the list element. You just can't do it by simply modifying the
value returned by the indexer or enumerator (and of course the latter
won't even compile, as already noted in this thread :) ).

Pete
 
bbg said:
Just to clarify, if I pass MyStruct to other method, its content will be
copied because it's a value type. What if I pass List<MyStruct> to other
method and other method call Add()?

List<T> is a reference type even if T is a value type - so the list
will be modified and that modification will be visible to the caller.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top