views:

80

answers:

4

I've been playing around trying to thoroughly understand Reference and Value types. Just when I thought I had it, I came across this scenario...

I created a class that would contain a single object.

class Container
{
    public object A {get; set;}
}

When I create an instance of this Container class (a) I am creating instance of a reference type. I assign an integer to the object within the class. As far as I am aware, this will be boxed as an object, another reference type.

int start = 1;  
Container a = new Container{ A=start };

I create another instance of the Container class (b), but assign the value of the first container to it, the value of b is now a reference to a.

Container b = a;

As expected when I print out the value of both a.A and b.A, they are the same.

Console.WriteLine("a.A={0},b.A={1}",a.A,b.A);
//a.A=1,b.A=1

And, as expected, when I change the value of a.A the value of b.A also changes due to them referencing the same object.

a.A = 2;

Console.WriteLine("a.A={0},b.A={1}",a.A,b.A);
// a.A=2,b.A=2

Now I decided to try this using individual local objects. Again, I box the integer into the first object and assign the value of the first object to the second. I believe the object, at this point, should be a reference type so c and d should reference the same object. Without changing anything they return the same value.

int start = 1;
object c = start;
object d = c;

Console.WriteLine("c={0},d={1}",c,d);
// c=1,d=1

Like before, when changing the value of the initial object, I expect the value of both objects to be the same.

c = 2;

Console.WriteLine("c={0},d={1}",c,d);
// c=2,d=1

When I print the result for these two objects, the value of d does not change like before.

Can someone please explain why the assignment is different in this scenario to the previous?

Thanks

+7  A: 

Here's your first mistake:

I create another instance of the Container class (b), but assign the value of the first container to it, the value of b is now a reference to a.

Container b = a;

That's not creating another instance. It's declaring another variable. Now both variables refer to the same object.

(I'll keep editing this answer as I keep reading...)

Next up:

int start = 1;
object c = start;
object d = c;
Console.WriteLine("c={0},d={1}",c,d); // c=1,d=1

Like before, when changing the value of the initial object, I expect the value of both objects to be the same.

c = 2;
Console.WriteLine("c={0},d={1}",c,d); // c=2,d=1

That's not changing an object it's changing a variable. Each of the assignments copies a value - except one of them also performs a boxing operation. Let's simplify it slightly:

object c = "first string";
object d = c;

Now there's no boxing involved - it'll just make it simpler to understand.

Both variables currently have values referring to the same object. That's due to the assignment, but there's nothing else linking the two variables. They happen to have the same value at the moment, but they're independent variables. Now let's change one:

c = "different string";

That has changed the value of c to refer to a different object. If you print out the values of c and d they will be "different string" and "first string" respectively. Changing the value of c doesn't change the value of d.


Now, let's go back to your previous scenario to see why it's different. There, you had:

a.A = 2;

That isn't changing the value of a at all. It's changing the data within the object a refers to.

Let's use a real world analogy to make this easier. Suppose all our variables are pieces of paper with house addresses written on them. The change of a.A = 2; is like changing the contents of the house at that address. Of course anyone else with the same address written on their piece of paper will see the change.

Now think of your c/d scenario. Again, imagine that we have two pieces of paper, and due to the assignment operator they both have the same address written on them. Now your assignment of a new value to the c variable itself is like scrubbing out the address on the c piece of paper and writing a different address on it. That doesn't change the d piece of paper at all - it still has the old address on, and if you visit that house, you'll see that nothing's changed. It's only what's written on the piece of paper that's changed.

Does that help?

Jon Skeet
That was a great help. Thanks!
fletcher
+2  A: 

In the first example you have this:

 a      b  
  \    /  
   \  /  
   |  |  
   v  v  
(Container)

There is only one container instance here. Both variables have a reference to this container. When you mutate the container both variables see the change.

However in this code:

object c = 1;
object d = c;

The second assignment doesn't mean "d is an alias for c", it just means that after the second assignment c and d point to the same object.

c = 2;

Now you reassign c to a new boxed integer so c and d are now pointing at two different objects. The two objects are not related in any way.

 Before                 After

 c      d               c                    d
  \    /         c=2    |                    |
   \  /          ---->  |                    |
   |  |                 |                    |
   v  v                 v                    v
(boxed integer 1)      (boxed integer 2)    (boxed integer 1)
Mark Byers
A: 

In your first scenario you had a reference to one object on the heap. And you had two variables ("a" , "b") referring to this same object. That is why when you change a member of this object you saw it get reflected on both variables.

In your second case you are doing something entirely different. When you did:

c = 2

You actually created a completely new object. The value type of int was converted into an object so a new reference object was created. At this point your "d" variable no longer refers to the same object as your "c" variable.

Matthew Manela
+3  A: 

The difference is the encapsulating object Container.

In the first case you have an object that contains a reference. When you said that you created a new instance of the Container class, you didn't. You just copied the reference to the existing object. As you have two references to the same object, you can change the contents of the object via one reference and read it via the other.

 a     b          a     b
  \   /            \   /
   \ /              \ /
---------        ---------
|       |        |       |
|   A   |        |   A   |
|   |   |        |   |   |
----|----   ->   ----|----
    |                |
---------        ---------
|       |        |       |
|   1   |        |   2   |
|       |        |       |
---------        ---------

In the second case you also have two references two the same object, but in this case you reference the boxed object directly, not a container. When you assign a new value to one of the references, you get two separate objects. One object is the boxed 1, and the other object is the boxed 2. When you assign a new value to b, it will not put the value in the box that it points to, it will create a new boxed object containing the new value.

 a     b             a          b
  \   /              |          |
   \ /               |          |
---------   ->   ---------  ---------
|       |        |       |  |       |
|   1   |        |   1   |  |   2   |
|       |        |       |  |       |
---------        ---------  ---------
Guffa