80

Consider this code:

public class Program
{
    private static void Main(string[] args)
    {
        var person1 = new Person { Name = "Test" };
        Console.WriteLine(person1.Name);

        Person person2 = person1;
        person2.Name = "Shahrooz";
        Console.WriteLine(person1.Name); //Output: Shahrooz
        person2 = null;
        Console.WriteLine(person1.Name); //Output: Shahrooz
    }
}

public class Person
{
    public string Name { get; set; }
}

Obviously, when assigning person1 to person2 and the Name property of person2 is changed, the Name of person1 will also be changed. person1 and person2 have the same reference.

Why is it that when person2 = null, the person1 variable will not be null either?

No Idea For Name
  • 11,411
  • 10
  • 42
  • 70

13 Answers13

184

Both person and person2 are references, to the same object. But these are different references. So when you are running

person2 = null;

you are changing only reference person2, leaving reference person and the corresponding object unchanged.

I guess the best way to explain this is with a simplified illustration. Here is how the situation looked like before person2 = null:

Before null assignment

And here is the picture after the null assignment:

Enter image description here

As you can see, on the second picture person2 references nothing (or null, strictly speaking, since reference nothing and reference to null are different conditions, see comment by Rune FS), while person still references an existing object.

Community
  • 1
  • 1
Andrei
  • 55,890
  • 9
  • 87
  • 108
  • 22
    In other words, person and person2 points to something, then person2 points to nothing by setting to null. – rro Aug 14 '13 at 10:42
  • 2
    @ShahroozJefriㇱ Basically, `person1` and `person2` are 2 different business cards that contain an address. If you do `person1 = person2` then `person1` is now a copy of `person2`. They are still *different* business cards, but they both contain the *same* address pointing towards the *same* object. The object itself does not change. – Nolonar Aug 14 '13 at 10:55
  • I'd remove the arrow on `person2` in the second picture because it references nothing - it's not a dangling reference. – Sameer Singh Aug 14 '13 at 11:02
  • @SameerSingh, done, thanks for response. I was thinking the same initially, but changed my mind for some reason. – Andrei Aug 14 '13 at 11:05
  • 2
    When using references, creating a variable and assigning it to a another pointer, creates a thing callled Alias, where, we have let's say, 3 variables, pointing to the same memory location. When you nullify one of those variables, there's one variable less pointing to that memory location, when all the variables point to NULL, the GC cleans that location to allocate new variable, at least in Java the GC works that way D: – NemesisDoom Aug 14 '13 at 13:59
  • Actually person2 does reference something it reference null and is there for a null reference. null is different from nothing and null == null which can only be the case if null something (otherwise the operation would logically translate to just the operator which makes no sense) doesn't change the explanation (much) but person2 points to null – Rune FS Aug 21 '13 at 07:14
55

Consider person1 and person2 as pointers to some location in storage. In the first step, only person1 is holding the address of the object from storage and later person2 is holding the address of memory location of object from storage. Later when you assign null to person2, person1 stays unaffected. That is why you see the result.

You may read: Value vs Reference Types from Joseph Albahari

With reference types, however, an object is created in memory, and then handled through a separate reference—rather like a pointer.

I will try to depict the same concept using the following diagram.

enter image description here

Created a new object of type person and the person1 reference (pointer) is pointing to the memory location in storage.

enter image description here

Created a new reference(pointer) person2 which is pointing to the same in storage.

enter image description here

Changed the object property Name to new value, through person2 since both references are pointing to the same object,Console.WriteLine(person1.Name); outputs Shahrooz.

enter image description here

After assigning null to person2 reference, it will be pointing to nothing, but person1 is still holding the reference to the object.

(Finally for memory management you should see The Stack Is An Implementation Detail, Part One and The Stack Is An Implementation Detail, Part Two from Eric Lippert)

Habib
  • 219,104
  • 29
  • 407
  • 436
14

You have changed person2 to reference null, but person1 isn't referencing there.

What I mean is that if we look at person2 and person1 before the assignment then both reference the same object. Then you assign person2 = null, so person 2 is now referencing a different type. It did not delete the object that person2 was referenced to.

I've created this gif to illustrate it:

enter image description here

No Idea For Name
  • 11,411
  • 10
  • 42
  • 70
13

Because you've set the reference to null.

When you set a reference to null, the reference itself is null.. not the object it references.

Think of them as a variable that holds an offset from 0. person has the value 120. person2 has the value 120. The data at offset 120 is the Person object. When you do this:

person2 = null;

..you're effectively saying, person2 = 0;. However, person still has the value 120.

Simon Whitehead
  • 63,300
  • 9
  • 114
  • 138
  • so person 2 and person has not same reference? –  Aug 14 '13 at 10:40
  • They reference the same object.. but they are separate references. This comes down to `copy-by-value` semantics. You're copying the value of the reference. – Simon Whitehead Aug 14 '13 at 10:40
4

Both person and person2 point to the same object. Therefore when you change the name of either one, both will get changed (since they point to the same structure in memory).

But when you set person2 to null, you make person2 into a null pointer, so that is does not point to the same object as person anymore. It wont do anything to the object itself to destroy it, and since person still points/references the object it wont get killed by garbage collection either.

If you also set person = null, and you have no other references to the object, it will eventually be removed by the garbage collector.

Øyvind Bråthen
  • 59,338
  • 27
  • 124
  • 151
2

person1 and person2 point to the same memory address. When you null person2, you null the reference, not the memory address, so person1 continues reffering to that memory address, that's the reason. If you change the Classs Person into a Struct, the behaviour will change.

pointnetdeveloper
  • 224
  • 1
  • 3
  • 7
  • They may or may not point to the same memory address (or any address, for that matter). What is important is that they *identify the same object*. On some kinds of concurrent garbage collector, it may be possible that while an object is being relocated some references may hold the old address while others identify the new one [code which writes to either address may have to block until all references were updated, and code which compares addresses would have to see if one address was "current" and the other wasn't and, if so, find the "new" address associated with the old one.] – supercat Jan 19 '15 at 20:31
2

I find it most helpful to think of reference types as holding object IDs. If one has a variable of class type Car, the statement myCar = new Car(); asks the system to create a new car and report its ID (let's say it's object #57); it then puts "object #57" into variable myCar. If one writes Car2 = myCar;, that writes "object #57" into variable Car2. If one writes car2.Color = blue;, that instructs the system to find the car identified by Car2 (e.g. object #57) and paint it blue.

The only operations which are performed directly on object ID's are creation of a new object and getting the ID, getting a "blank" id (i.e. null), copying an object ID to a variable or storage location that can hold it, checking whether two object IDs match (refer to the same object). All other requests ask the system to find the object referred to by an ID and act upon that object (without affecting the variable or other entity that held the ID).

In existing implementations of .NET, object variables are likely to hold pointers to objects stored on a garbage-collected heap, but that's an unhelpful implementation detail because there's a critical difference between an object reference and any other kind of pointer. A pointer is generally assumed to represent the location of something which will stay put long enough to be worked with. Object references don't. A piece of code may load the SI register with a reference to an object located at address 0x12345678, start using it, and then be interrupted while the garbage collector moves the object to address 0x23456789. That would sound like a disaster, but the garbage will examine the metadata associated with the code, observe that the code used SI to hold the address of the object it was using (i.e. 0x12345678), determine that object that was at 0x12345678 had been moved to 0x23456789, and update SI to hold 0x23456789 before it returned. Note that in that scenario, the numerical value stored in SI was changed by the garbage collector, but it referred to the same object before the move and afterward. If before the move it referred to the 23,592nd object created since program startup, it will continue to do so afterward. Interestingly, .NET does not store any unique and immutable identifier for most objects; given two snapshots of a program's memory, it will not always be possible to tell whether any particular object in the first snapshot exists in the second, or if all traces to it have been abandoned and a new object created that happens to look like it in all observable details.

supercat
  • 77,689
  • 9
  • 166
  • 211
1

person1 and person2 are two separate references on the stack that point to the same Person object on the heap.

When you delete one of the references, it is removed from the stack and no longer points to the Person object on the heap. The other reference remains, still pointing to the existing Person object on the heap.

Once all references to the Person object are removed, then eventually the Garbage Collector will removed the object from memory.

Ryan Spears
  • 2,963
  • 2
  • 31
  • 39
1

When you create a reference type its actually copying a reference with all objects pointing to the same memory location , However If you have assigned Person2=Null it will have no effect as person2 is just a copy of reference person and we have just erased a copy of reference .

Suraj Singh
  • 4,041
  • 1
  • 21
  • 36
1

Note that you can get value semantics by changing to a struct.

public class Program
{
    static void Main()
    {
        var person1 = new Person { Name = "Test" };
        Console.WriteLine(person1.Name);

        Person person2 = person1;
        person2.Name = "Shahrooz";
        Console.WriteLine(person1.Name);//Output:Test
        Console.WriteLine(person2.Name);//Output:Shahrooz
        person2 = new Person{Name = "Test2"};
        Console.WriteLine(person2.Name);//Output:Test2

    }
}
public struct Person
{
    public string Name { get; set; }
}
Mark Hurd
  • 10,665
  • 10
  • 68
  • 101
0
public class Program
{
    private static void Main(string[] args)
    {
        var person = new Person {Name = "Test"};
        Console.WriteLine(person.Name);

        Person person2 = person;
        person2.Name = "Shahrooz";
        Console.WriteLine(person.Name);//Output:Shahrooz
        // Here you are just erasing a copy to reference not the object created.
        // Single memory allocation in case of reference type and  parameter
         // are passed as a copy of reference type .   
        person2 = null;
        Console.WriteLine(person.Name);//Output:Shahrooz

    }
}
public class Person
{
    public string Name { get; set; }
}
Suraj Singh
  • 4,041
  • 1
  • 21
  • 36
  • @doctorlove forgot the comments actually:-( , Well For reference type only one memory space is allocated and Person 2 or Person 1... are just copy of that reference type which are pointing toward the memory location , Erasing a copy of reference type will not affect anything . – Suraj Singh Aug 14 '13 at 11:01
0

You first copy the reference to person1 to person2. Now person1 and person2 refer to the same object, which means modifications to the value of that object (i.e changing the Name property) can be observed under both variables. Then, when assigning null, you are removing the reference you just assigned to person2. It is only assigned to person1 now. Note that the reference itself is not changed.

If you had a function that accepted an argument by reference, you could pass reference to person1 by reference, and would be able to change the reference itself:

public class Program
{
    private static void Main(string[] args)
    {
        var person1 = new Person { Name = "Test" };
        Console.WriteLine(person1.Name);

        PersonNullifier.NullifyPerson(ref person1);
        Console.WriteLine(person1); //Output: null
    }
}


class PersonNullifier
{
    public static void NullifyPerson( ref Person p ) {
        p = null;
    }
}

class  Person {
    public string Name{get;set;}
}
Asad Saeeduddin
  • 46,193
  • 6
  • 90
  • 139
0

Saying:

person2.Name = "Shahrooz";

follows the reference of person2 and "mutates" (changes) the object that the reference happens to lead to. The reference itself (the value of person2) is unchanged; we still refer the same instance at the same "address".

Assigning to person2 as in:

person2 = null;

changes the reference. No object gets changed. In this case the reference arrow is "moved" from one object to "nothing", null. But an assignment like person2 = new Person(); would also only change the reference. No object is mutated.

Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181