Question:
What is the main difference between reference and value data types in C#?
Answer:
The main difference between reference and value data types is the meaning of equality and copy.
For value types, when you copy, you get a new instance containing copies of the original instance's values. In other words, instances behave like values , like numbers, for example. If you copied a value and changed the original value, then these changes will not be reflected in the copy in any way:
Point p1 = new Point(1, 2);
Point p2 = p1; // (1, 2)
p1.X = 100;
// p2.X всё ещё равно 1
For reference types, copying gives you a new reference to the same data. Accordingly, if you change an object of a reference type, then these changes become available through any of the references to the object:
XElement e1 = new XElement("test") { Value = "hello" };
XElement e2 = e1;
e1.Value = "goodbye";
// e2.Value стало равно "goodbye"
Now, equality. The same instances of value types, whether they are copies of the same common object or not, are equal. Instances of reference types, even if they contain the same data, are different.
Point p1 = new Point(1, 2), p2 = new Point(1, 2);
// p1 == p2
XElement e1 = new XElement("test") { Value = "hello" },
e2 = new XElement("test") { Value = "hello" };
// e1 != e2
We can say that objects of reference types have personality, while objects of value types do not.
Of course, the comparison semantics can be customized by overloading the comparison operator (and/or implementing the IEquatable<T>
interface). You can, for example, make a reference type behave as if it were a value type. Conversely, by storing value type data in an immutable field of a reference type, you can make the value type behave like a reference type.
All other differences are only consequences of these main differences. The fact that reference type variables in the Microsoft implementation store a reference internally is essentially an implementation detail. That instances of value types sometimes end up on the stack (namely, when they are not class fields, and do not lie in a closure, async
method, or generator method) is also an implementation detail (and may not be the case in future versions or other implementation of the .NET platform).
When you choose to use a struct (value type) or a class (reference type) for your object, ask yourself the question: Are two objects with the same data the same or different? If these are, for example, objects representing a person, and in the data you have his first name, last name, and age, then it makes sense to use a class: after all, two people with the same name are still not the same person.
And if this is, for example, an object representing a rational fraction, then there is no point in separating different instances of the fraction 3/8, so it is appropriate to use the structure here.