Why doesn't the constructor affect the properties in this example
I'm wondering why, when we have p = new Person("TOM", 999);
, calling fred.PrintInfo();
It doesn't change p to TOM and 999, but using p.age = 99; we can change the age of fred and both constructors and properties are public, then, what's here that I'm missing? I don't want to do anything with this code, I just need a reason.
using System;
class Person
{
public string fullName;
public int age;
public Person(string n, int a)
{
fullName = n;
age = a;
}
public void PrintInfo()
{
Console.WriteLine("{0} is {1} years old", fullName, age);
}
}
class MainClass
{
public static void SendAPersonByValue(Person p)
{
p.age = 99;
p = new Person("TOM", 999);
}
public static void Main()
{
Person fred = new Person("Fred", 12);
fred.PrintInfo();
SendAPersonByValue(fred);
fred.PrintInfo();
}
}
fred
points to a specific location in memory:
+------------+
fred ----> | Fred 12 |
+------------+
When calling SendAPersonByValue p
points to the same location:
+------------+
fred ----> | Fred 12 |
+------------+
^
p ---------+
p.age = 99;
now changes the value in memory:
+------------+
fred ----> | Fred 99 |
+------------+
^
p ---------+
whereas it new Person("TOM", 999);
creates a new Person in memory and p = ...
points p
to it:
+------------+
fred ----> | Fred 99 |
+------------+
+------------+
p ----> | TOM 999 |
+------------+
And that's why fred
it still contains Fred, 99
.
Now, if you need to pass fred
as a parameter ref
, p
it will be an alias for fred
:
+------------+
fred/p ----> | Fred 12 |
+------------+
After p.age = 99
:
+------------+
fred/p ----> | Fred 99 |
+------------+
After p = new Person("TOM", 999);
:
+------------+
| Fred 99 | (will be garbage collected eventually)
+------------+
+------------+
fred/p ----> | TOM 999 |
+------------+
Because the link Person p
is simply passed not by the link link .
You need to do this in order to transfer the Personality by reference .:
public static void SendAPersonByValue(ref Person p)
{
p.age = 99;
p = new Person("TOM", 999);
}
If you want the person's link in the main text to change to a new link, you need to put the ref in the parameter list. Otherwise, the stack has a reference to where it is primarily used. It can change internal properties, but not the link that belongs to the main element that it passed.
This is because you created a new link. If you want the behavior you expected, you will have to change the SendAPersonByValue method to ref Person p
.
So here's what's going on here:
SendAPersonByValue(fred);
//You are passing the value of fred reference here
...
public static void SendAPersonByValue(Person p)
{
p.age = 99;
//The value of Fred reference is used to populate his age
p = new Person("TOM", 999);
//p is now assigned to a totally new reference.
//Because value are immutable, fred remains
//However, p is now pointing to a different reference value
}
What happens when you add ref to your method signature:
SendAPersonByValue(ref fred);
//A reference (pointer) to Fred reference value is passed
...
public static void SendAPersonByValue(Person p)
{
p.age = 99;
//The pointer of the reference of Fred is used to populate his age
//Ultimately, this is the same as above because the both end up at Fred
p = new Person("TOM", 999);
//The reference value of p (fred) is now the new reference
//since it was just a pointer to the reference value
}
Here is a good article on meaning and links
When you assign a variable p in a method SendAPersonByValue(Person p)
, you are only assigning it locally. It doesn't change fred
in your main method.
You can revert the changed person and replace fred in the main method, or use a keyword ref
.