It is important to understand that C# distinguishes between two categories of data type — value and reference types. The main difference is that a value type stores value directly and reference type stores a reference to its value. Value types are stored in the stack, and reference types are stored in the managed heap.
Also, assignment operation have different behavior on value types and reference types. Consider the following examples:
void Main() { int i = 5, j; // Declaring value types. j = i; // Value type copied by value. // As a result, two separate int values are in memory. Write(i, j); j = 8; // The value of i variable doesn't change. Write(i, j); } public void Write(int i, int j) => Console.WriteLine($"i: {i}, j: {j}");
Result:
i: 5, j: 5
i: 5, j: 8
void Main() { SomeType i = new SomeType() // Declaring and instantiating { value = 15 }; // reference type. SomeType j = i; // Only reference to object copied, // no instantiating occurs. // As a result, there is only one SomeType object // i and j both point to the same memory location. Write(i, j); j.value = 22; // The value of i object changes. Write(i, j); } public class SomeType { public int value; } public void Write(SomeType i, SomeType j) => Console.WriteLine($"i: {i.value}, j: {j.value}");
Result:
i: 15, j: 15
i: 22, j: 22
If a variable is a reference, it is possible to set a null reference, one that does not refer to any object. If a reference is set to null, then it is possible call only static members functions or fields against it. Also, if you try to call any nonstatic member functions or fields, you’ll get an exception at runtime.
i = null;
Most of the complex C# data types, including classes that you declare, are reference types. They are allocated upon the managed heap. If you want to define own complex value type, you should declare it as a struct.