views:

251

answers:

3

Hi

We all know that generic List<> does not box value types. Why on the following code snippet the rects[1] is not affected by Inflate method?

If there is no boxing and I want to afect the rect[1] I need to write three lines of code as it is shown - commented. Can someone please explain this?

List<Rectangle> rects = new List<Rectangle>();

for (int i = 0; i < 5; i++)
{
    rects.Add(new Rectangle(1, 1, 1, 1));
}

foreach (Rectangle item in rects)
{
    Console.WriteLine(item);
}

//Rectangle r = rects[1];
//r.Inflate(100, 100);
//rects[1] = r;

rects[1].Inflate(100, 100);

foreach (Rectangle item in rects)
{
    Console.WriteLine(item);
}
+1  A: 

Yes, the value of the Rectangle will be copied to your local variable if you do that.

It's not called boxing. It's just normal copying, since Rectangle is a value type.

Mehrdad Afshari
+1  A: 

Because Rectangle is a value type, and calling rects[1] makes a copy of the Rectangle instance at index 1. That's just the way that value types work in .NET, really.

Andy
+7  A: 

It isn't boxing - simply that when you get the rectangle out, you are manipulating a standalone copy of the rectangle.

This is one of the side effect of having mutable value-types (structs). And the reason you shouldn't write your own mutable structs - it is too easy to lose data.

Since you can't make the pre-built rectangle immutable, I'm afraid you're going to have to copy it out; mutate it; and put it back in.

Marc Gravell
Could you please explain what goes where in terms of location of elements in stack and heap? I still have problems understanding it...
pkolodziej
The value of rects[1] is *copied* onto the stack. Then Inflate is called on this *copy* and the value in the list remains unaffected.
Jon Skeet
When exactly the *copy* is used? What I mean: when I write rect[1].Inflate the *copy* is used. Why then, when I write rect[1] = new Rect(100,100) the new value replaces the value on the heap but not *copy* on the stack?What are the rules of creating a *copy* ?
pkolodziej
The only time you update an existing struct value is when you work with a *field*, a *local variable*, or an array element. In this case, you are calling a **method** (the [int] indexer); this uses the stack to return a new **copy** of the rectangle into the local stack (essentially a local...
Marc Gravell
variable). So when you call rects[1].Inflate(...) you are mutating the value on your stack, then discarding it.
Marc Gravell
Did you mean: working with field - rect[1].field ? Also how do you know the difference (when I access field of value type I work on element on heap directly but when I call a method I work on *copy*) - Do you know any book/article which explains this?
pkolodziej
Re "did you mean" - no. I mean if you had a class with a Rectangle field, and called Inflate **on the field**, then the field is updated. The clone happens when you pass the struct around, for example as a method argument, or as a return. A list indexer (rects[1]) is a method, so a return value.
Marc Gravell