Skip to content

Reference & value types

How reference types and value types behave differently.

It is important to understand the differences between reference types and value types as how they are passed around and stored in memory is very different and it can affect both how your code behaves and how well it performs.

Reference types

Reference types are declared using the class or record keywords. When an instance is created memory is allocated in an area known as the "heap" that is available to all functions, classes and methods providing they have the memory address where it lives within the heap.

It is this memory address - also known as a pointer - that is actually stored in the variable.

Assigning a reference type to another variable either directly using = or indirectly by passing it to another function will simply point both variables at the same instance on the heap.

This is known as "pass by reference" and further modification made to the state of the object will be reflected on both sides as they are pointing to the same object.

The next time the .NET Garbage Collector (GC) runs it will look at all the objects on the heap and free any that are no longer referenced by anything else in the program. (With some caveats for performance where large objects or objects that have been around for a long time are not checked as often).

Value types

Value types can be declared by the struct keyword and C# has a number of them built in like int, byte, float etc.

These types store their actual value in the variable and so assigning or passing it will simply create another copy of the struct with its value. This is known as "pass by value" and modifications made through one variable will not be reflected in the other as they are two distinct instances.

Value types store their values either inside a reference type in which case they will be lost when the reference type is garbage collected or they will be stored in the block of memory allocated upon the stack.

Every time you call a function a new block of memory is allocated on the stack to store the values of the parameters and any local variables. This is known as a "stack frame" and is lost as soon as the function exits.

The .NET Garbage Collector does not get involved in directly freeing value types as they either belong to a reference type (which will be collected) or a stack frame (which will be freed immediately upon exiting the function it was created for).