The problem is a restriction on the foreach construct: it doesn't allow
modifications to the elements you are iterating on, see [1].

Interestingly, if you define Foo as a class instead of a struct then Foo
[0].Bar can be modified and has the correct "bar" value after the loop. If
Foo is an struct it doesn't. Check this code:

class Foo
{
 string _bar;
 public string Bar
 {
  get{return _bar;}
  set{_bar = value;}
 }
}

class myApp
{

 static void Main()
 {
  Foo[] foos = new Foo[1];
  foos[0] = new Foo();

  foreach(Foo foo in foos)
   foo.Bar = "bar";
  Console.WriteLine(foos[0].Bar);

 }
}

The reason for this behaviour, I think, is because when I have Foo as an
struct if I try to assign "bar" to foo.bar, I'm changing the element and
that's why the compiler complains. You can fool the compiler by using an
object to iterate and then cast it to a Foo object. The compiler canno't
detect that you are modifying the collection and so I doesn't complain at
compile time.

When Foo is implemented as a class then the element I iterate on is a
reference. In this case, what I can't do is to assign a diferent value to
it, for example foo = null, but I can modify the content of the object
referenced.

[1] http://msdn.microsoft.com/library/default.asp?url=/library/en-
us/csref/html/vclrftheforeachstatement.asp?frame=true

FedeR

>Jim,
>
>I was able to get the last example to compile with the following
>modification:
>
>Foo[] foos = new Foo[1];
>foos[0] = new Foo();
>
>foreach(Object obj in foos) {
>    Foo foo     = (Foo) obj;
>    foo.Bar = "bar";
>}
>//but foos[0].Bar is still "" here...
>
>
>
>Note that difference is that the type of the iteration variable is
>Object. In order to get to the Bar property you have to cast it to the
>Foo type.
>
>Even with this "solution", the value of Bar is not being updated (as
>Brad Wilson indicated).
>If you check the value of foos[0].Bar after the foreach loop, you will
>find that it is still a zero length string, and NOT "bar".  This is due
>as you suspected to some "boxing" that is going on.
>
>The gist is that you can't modify a value type while iterating on an
>array or collection using foreach.
>
>HTH
>
>Larry
>
>
>**********************
>Snippets from Original Message ...
>**********************
>
>Can someone help me get my tiny brain around this?  Given this struct:
>
>struct Foo {
>
>    string _bar;
>
>    public string Bar {
>        get{return _bar;}
>        set{_bar = value;}
>    }
>}
>
>Why can I do this:
>
>Foo foo = new Foo();
>foo.Bar = "bar";
>
>[Brent]
>This changes the Bar property in the Foo valuetype, as expected.
>
>and this:
>
>Foo[] foos = new Foo[1];
>foos[0] = new Foo();
>
>for(int i=0;i<foos.Length;i++) {
>    foos[i].Bar = "bar";
>}
>
>[Brent]
>This changes the Bar property in the Foo valuetype in the array element,
>as expected.
>
>
>but *not* this:
>
>Foo[] foos = new Foo[1];
>foos[0] = new Foo();
>
>foreach(Foo foo in foos) {
>    foo.Bar = "bar";
>}
>
>[Brent]
>Variable 'foo' contains a *copy* of the array element. Foo is a
>valuetype. You would be changing the Bar property in the copy and never
>affecting the element in the array.
>
>
>The compiler error is distinctly unhelpful: "The left-hand side of an
>assignment must be a variable, property or indexer" - which it plainly
>is. I understand why I can't modify the value of an intermediate
>expression (CS1612), but what's the deal here?  Am I getting my
>pass-by-value and pass-by-ref's muddled?
>
>You can read messages from the DOTNET archive, unsubscribe from DOTNET, or
>subscribe to other DevelopMentor lists at http://discuss.develop.com.

You can read messages from the DOTNET archive, unsubscribe from DOTNET, or
subscribe to other DevelopMentor lists at http://discuss.develop.com.

Reply via email to