Reputation: 13
I was going through differences between Stack and Heap and tried something to understand the reference type and value type variables. What I understood is that a reference type holds the address of value stored in heap and if the value in heap is changed all the reference to that address will point to new value, so I played with below codes but I am wondering why the below code prints a value of 10
when lt1
is set to null
:
public void Method4()
{
List<int> lt1 = new List<int>() { 1, 2, 3, 4 };
List<int> lt2 = lt1;
lt1 = null;
int xx = lt2.Sum();
Console.WriteLine(xx);
}
But it prints 0
when lt1
is cleared.
public void Method4()
{
List<int> lt1 = new List<int>() { 1, 2, 3, 4 };
List<int> lt2 = lt1;
lt1.Clear();
int xx = lt2.Sum();
Console.WriteLine(xx);
}
I think lt2
holds the reference of lt1
and when lt1
is set to null then lt2
should also be null but this is not the case.
What is going on in the background?
Upvotes: 1
Views: 212
Reputation: 2226
You've got a couple of ideas conflated here. For a conceptual overview, I would highly recommend Jon Skeet's Variables, References, and Objects explanation here.
We can also examine each example and comment on what is going on:
lt1
:We can break down the following method
public void PrintsTen()
{
List<int> lt1 = new List<int>() { 1, 2, 3, 4 };
List<int> lt2 = lt1;
lt1 = null;
int xx = lt2.Sum();
Console.WriteLine(xx);//prints "10"
}
line by line.
First:
List<int> lt1 = new List<int>() { 1, 2, 3, 4 };
lt1
of reference type List<int>
inside the PrintsTen
scope.List<int>
which contains the values 1
, 2
, 3
, and 4
lt1
to reference/"point to" the object on the heap we just created with the new
portion of the expressionSecond:
List<int> lt2 = lt1;
lt2
of reference type List<int>
inside the PrintsTen
scope.lt2
to reference/"point to" the object on the heap that lt1
references/"points to".
lt2
references/"points to" the List<int>
on the heap.new
call in the previous line.Third:
lt1 = null;
lt1
variable to reference/"point to" null
(nothing).
lt2
points to!Fourth:
int xx = lt2.Sum();
xx
of value type int
inside the PrintsTen
scope.xx
equal to the value returned from the invocation of the Sum()
method on the object referenced by lt2
lt2
references/"points to" the list object which contains 1
, 2
, 3
and 4
the result will be the value 10
.Finally:
Console.WriteLine(xx);
WriteLine
method prints the value of xx
(which is 10
) to the console windowClear()
on lt1
:We can follow a similar explanation for the following method
public void PrintsZero()
{
List<int> lt1 = new List<int>() { 1, 2, 3, 4 };
List<int> lt2 = lt1;
lt1.Clear();
int xx = lt2.Sum();
Console.WriteLine(xx);//prints "0"
}
line by line as well.
First:
List<int> lt1 = new List<int>() { 1, 2, 3, 4 };
lt1
of reference type List<int>
inside the PrintsZero
scope.List<int>
which contains the values 1
, 2
, 3
, and 4
lt1
to reference/"point to" the object on the heap we just created with the new
portion of the expressionSecond:
List<int> lt2 = lt1;
lt2
of reference type List<int>
inside the PrintsZero
scope.lt2
to reference/"point to" the object on the heap that lt1
references/"points to"
lt2
references/"points to" the List<int>
on the heap.new
call in the previous line.Third:
lt1.Clear();
lt1
references/"points to" the list object which contains 1
, 2
, 3
and 4
, Clear()
will empty the list object on the heap to contain no elements.
lt2
also points to the that same object, lt2
references/"points to" the empty list.Fourth:
int xx = lt2.Sum();
xx
of value type int
inside the PrintsZero
scope.xx
equal to the value returned from the invocation of the Sum()
method on the object referenced by lt2
lt2
references/"points to" the list object which contains no elements the result will be the value 0
.Finally:
Console.WriteLine(xx);
WriteLine
method prints the value of xx
(which is 0
) to the console windowUpvotes: 0
Reputation: 19096
A variable storing a reference of an instance is like a sheet of paper where you write down the adress of a building.
If I make a copy of the sheet of paper then I have 2 sheets (variables) with the same address (reference) but I will not have 2 buildings (instances).
If I paint the building (instance) at the adress (reference) on the copied sheet of paper (variable) in red (setting a property of the instance) also the building (instance) at adress (reference) of the original (variable) will be red, because it was the same building (instance).
Upvotes: 0
Reputation: 8452
When you set lt1
to null
you are not changing the value of that variable but only the reference.
With lt1 = null
you are telling the compiler that lt1
should no longer point to the List<int>
you created in the first line of Method4()
but should instead point to nothing (i.e. null
).
The difference between lt1 = null
and lt1.Clear()
is exactly that the first changes the reference lt1
itself whereas the latter changes the value that lt1
is referencing.
Upvotes: 0
Reputation: 62213
lt1 = null; // changing the pointer of lt1 does not also change that pointer of lt2
In your 1st example all you are doing is changing the pointer to the List<int>
for variable lt1
. lt2
still points to that List<int>
so it will return 10 when Sum
is called. You have not made any actual changes to the underlying object on the heap, only changed the address of lt1
so it no longer points to that object.
lt1.Clear(); // its the same list on the heap but now you are calling a method on that list called Clear which removes the items on that list.
In your 2nd example you have a single List<int>
(again) but you call the member on that object to clear its content which will result in an empty list. You have changed the underlying object, not the pointer to that object. This is why the Sum will return 0. An empty list is not the same as a null pointer.
Upvotes: 0
Reputation: 35270
When you do this:
List<int> lt2 = lt1;
You're assigning lt2
to point to the same location in memory that lt1
is currently pointing to. If you then go on to do this:
lt1 = null;
That only means that lt1
no longer points to any location in memory -- more importantly it no longer points to the same location in memory that it previously shared with lt2
; however lt2
still points to that location, which is why lt2.Sum()
still works.
Clearing the memory reference that lt1
points to does not affect lt2
.
Upvotes: 1
Reputation: 20224
If you edit the list items "in lt1", they are also edited "in lt2", because they are edited in the list, not in the pointer.
But you are telling "lt1 should now point to null". This does not mean that lt2 should point to null.
Upvotes: 4