Why strings behave like ValueType

You're not changing anything about the object a1 points to, but instead changing which object a1 points to.

a = new Person(); b = a; b = new Person();
(source: morethannothing.co.uk)

Your example replaces "new Person { … }" with a string literal, but the principle is the same.

The difference comes when you're changing properties of the object. Change the property of a value type, and it's not reflected in the original.

a = new Person(); b = a; b.Name = …;
(source: morethannothing.co.uk)

Change the property of a reference type, and it is reflected in the original.

a = new Person(); b = a; b.Name = …;

p.s. Sorry about the size of the images, they're just from something I had lying around. You can see the full set at http://dev.morethannothing.co.uk/valuevsreference/, which covers value types, reference types, and passing value types by value and by reference, and passing reference types by value and by reference.


Whenever you see

variableName = someValue;

that's changing the value of the variable - it's not changing the contents of the object that variable's value refers to.

This behaviour of string is entirely consistent with other reference types, and has nothing to do with immutability. For example:

StringBuilder b1 = new StringBuilder("first");
StringBuilder b2 = b1;
b2 = new StringBuilder("second");

That last line doesn't change anything about b1 - it doesn't change which object it refers to, or the contents of the object it refers to. It just makes b2 refer to a new StringBuilder.

The only "surprise" here is that strings have special support in the language in the form of literals. While there are important details such as string interning (such that the same string constant appearing in multiple places within the same assembly will always yield references to the same object) this doesn't affect the meaning of the assignment operator.


They don't. You changed the pointer of a2, not the object it pointed to.
When you are using classes and getting your expected behavior, you must be setting a property of the object, not its reference.

Any other class will behave the same:

Foo a = new Foo(1);
Foo b = a; //a, b point to the same object

b.Value = 4; // change property
Assert.Equals(a.Value, 4); //true - changed for a

b = new Foo(600); // new reference for b
Assert.Equals(a.Value, 4); //true
Assert.Equals(b.Value, 600); //true

Tags:

C#

String