Reputation: 884
string is an reference type (similar to object). Why the diff behavior in the following scenario - when creating an instance of string and object?
I understand the concept of immutability etc in string but that is to do with value assigned to string (which wont change). My question is more to do with why s2 is creating another complete instance whereas if i had done with object it doesnt do that?
Example of string
class Program
{
static void Main(string[] args)
{
string s1 = "Hello";
string s2 = s1;
s1 = null;
Console.WriteLine("s1 = " + s1);
Console.WriteLine("s2 = " + s2);
Console.ReadLine();
}
}
Output: s2 still prints "Hello"
Example of object
class Program
{
static void Main(string[] args)
{
Name s1 = new Name();
s1.id = 5;
Name s2 = s1;
s1 = null;
Console.WriteLine("s1 = " + s1.id);
Console.WriteLine("s2 = " + s2.id);
Console.ReadLine();
}
}
public class Name
{
public int id { get; set; }
}
Output: Both s1 and s2 are null;
Upvotes: 3
Views: 275
Reputation: 2185
First of all, I'm sure you realize you are going to get a lot of NullReferenceException
with that code you've got there.
Secondly, the string class is a fun and special object
which you can mentally think of as a primitive
data type. Let me just say though, System.String
is not a primitive type.... but you can think about it like it is for general purposes.
I have drawn some diagrams that may better display what is going on in each of the examples you gave.
When you
string s1 = "Hello";
string s2 = s1;
An object named s1 of type string is created which points to a memory space that holds the value "Hello"
. Then a new object of type string named s2 is created and the value of "Hello"
from s1 is copied to another memory space and addressed by s2
. This results in your memory looking as it does below.
Now, when you
s1 = null;
You have set the reference of s1
to point to the null pointer
, which leaves that memory with the value of "Hello" to float around until the garbage collector comes around and removes it. At this point s1
is still pointing to the value that was copied earlier.
Now lets look at how this differs from a normal object.
When you
Name s1 = new Name();
s1.id = 5;
Name s2 = s1;
A memory space called s1
is created which points to a newly allocated space in memory the large enough to hold the memory of a Name
object.
Then you pointed to the memory space addressed in s1
and changed its Property called id
to be 5.
Then you created a new memory space called s2
which copies the value of the address to the allocated memory mentioned above. So now s1
and s2
point to the same object in memory. This results in your memory looking as it does below.
Note: That at this point, if you were to change the value of id
it would change for both s1
and s2
, and if that was nullable type you were using for id
, and you set it to be null
, the change would be reflected in both s1
and s2
.
Now if you were to
s1 = null;
as you did in your post. s1
would change its address to point to the null pointer
, and s2
would continue to be looking at the same space in memory. Which results in what you see below.
Edit: To provide further explanation on why strings seem to behave like primitives and not like objects.
I don't know, however I will speculate.
My guess is that it was designed this way because developers want to use string
as if it were a primitive. I will say that it is so nice to not have to worry about stray references to my string, or having to clone
it before I make changes to it. However Microsoft could not make string
a primitive type because strings are huge. The max length of a string
is the maximum value of an Int32
. And every letter is a char
which is a Int16
, so that is somewhere around 2 gigs I believe (don't quote my math). This means that strings are too big to store on the stack which is only 1MB. Thus strings have to be objects and placed on the heap.
If you are unaware of the differences between the stack and the heap, I do recommend looking it up, it is good to know how memory is handled, while in C# all that dirty work is done for you, if you decide to move to something like C++ you'll find yourself managing that on your own (not super fun).
So in short I suppose it is because we want to use strings like primitives, but they are implemented as objects because of the limitations of the stack.
Upvotes: 6
Reputation: 101681
In your second code snippet, s2
is not null but you can't see it because s1.id
throws NullReferenceException
and you didn't handle it.So your program never reaches the second line.Remove it and you will see 5
in the Console
.After s1 = null;
, s2
doesn't change.It is still pointing the s1's
old Location in the memory.
Also if you don't want to remove the first line alternatively you can put it inside of try/catch
block:
try
{
Console.WriteLine("s1 = " + s1.id);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.WriteLine("s2 = " + s2.id);
And you can see 5
in the Console
and the exception message.But this is completely unnecessary; it will definitely throw exception because s1
is null.So, as a result: there is no difference between string
and other reference types except immutability
Upvotes: 7
Reputation: 7961
Your variables are reference variables because they are declared with the String
type. Your code is doing this:
Hello
s1
s2
and assign its reference to the reference in s1
s1
's reference to null
Throughout this process, the string Hello
is still in memory and s2
still references it. s1
and s2
are two different containers; they can each hold their own reference. Whether the reference is the same is immaterial.
I'll try a metaphor:
You (s2
) and I (s1
) are sitting in a room looking at a vase (Hello
) on a table. This is akin to s1
and s2
pointing to the same string in memory, as they do after the second line in your Main
method. Now, I get up and leave the room (s1 = null;
) You're still looking at the vase, aren't you? We are two different people (variables) with the capacity to look at different things.
Upvotes: 1