Skip to content
Theory · Value and reference types · lesson 1 of 11

Two kinds of types

What you'll learn

What you'll learn:

  • tell value types from reference types and predict what an assignment copies;
  • declare structs and pass structs and objects to methods, with and without ref;
  • explain boxing and work with null through ?., ??, int? and string?;
  • compare objects and structs with == and Equals.
Under int, two separate sheets with only the copy scribbled on; under Counter, two links lead to one scribbled document

A colleague needs your report, and there are two ways to hand it over. You can make a photocopy: the colleague gets a sheet of their own and can scribble all over it, while yours stays the same. Or you can send a link to the document in the cloud. Then you both open one and the same document, and each sees the other's edits at once.

In C#, an assignment and passing an argument to a method also work in one of these two ways. You have met both already: numbers were copied on assignment, while arrays and objects of classes got a second name. Let's look at them side by side:

C#
int a = 1;
int b = a;
b = 2;
Console.WriteLine(a); // 1

Counter first = new Counter();
Counter second = first;
second.Value = 2;
Console.WriteLine(first.Value); // 2

class Counter
{
    public int Value;
}

Let's go through the example line by line:

  1. int b = a; makes a photocopy: b gets a number 1 of its own.
  2. b = 2 changes only b, so a still prints 1.
  3. Counter second = first; copies not the object but the reference to it. This is a second link to the same document.
  4. second.Value = 2 changes the shared object, and first.Value prints 2 too.

One and the same assignment = gave two different results.

Here the analogy is inexact. In real life, a photocopy is made only when someone asks for it. In C#, an assignment gives you no choice: whether the value or the reference is copied is decided by the type, not by whoever writes =.

Value types

A variable of a value type holds the value itself. Assignment and passing to a method copy it whole, and the copies live separately from then on. You have met this behavior already:

  • in the theme on variables: after b = a and a = 7, b still held the old number;
  • in the theme on methods: a method got a copy of the argument, and the variable itself through ref.

The value types are all the built-in numeric types (int, long, double, decimal), plus bool and char. You can declare a value type of your own as well, but with the word struct, not class.

Reference types

A variable of a reference type holds a reference, and the object itself lies elsewhere in memory. Assignment copies the reference, and the object gets one more name. You know reference types too:

  • arrays: after int[] b = a; one array has two names;
  • string: several variables can share one string, and that is safe because nobody can change the string;
  • all classes: a variable of a class holds a reference to an object.

The kind of a type is set by its declaration. A type declared with the word class is always a reference type, and no assignment will turn its object into a photocopy.

The stack and the heap

Where does the difference come from? To see it, let's look at where a program keeps its data. The simplified picture is this:

  • the stack is the call stack you already know: each call gets its own area there for its local variables and frees it when the method finishes;
  • the heap is shared memory for objects: an object created with new lies there as long as at least one reference leads to it.

When no references to an object are left, the garbage collector—a part of the .NET runtime—frees its memory in due course.

A local int variable holds the number right on the stack. A local variable of a class holds only the reference on the stack, and the object itself lies on the heap. This is the link to the cloud document: there is one document, and there can be any number of links to it.

In the course's widgets, each method call is drawn as a box of its own—that is its area on the stack. The arrow → next to a variable leads to an object on the heap.

Where a value type is stored

The picture of the stack and the heap is simplified. A field of a value type lies inside its object, that is, on the heap, and in some language constructs the compiler moves local variables to the heap too.

Interviewers like to ask: "Are value types stored on the stack?" The precise answer is that a value type is stored where the variable is declared:

  • a local variable of a method usually lies on the stack;
  • a field of an object lies on the heap together with the object;
  • an element of an int[] array lies inside the array, on the heap too.

What sets a value type apart from a reference type is what an assignment copies, not the place in memory.

Common mistake

Before resetting a counter, someone decided to save a "backup copy" with =:

C#
Counter current = new Counter();
current.Value = 5;
Counter backup = current; // ✗
current.Value = 0;
Console.WriteLine(backup.Value); // 0

The program compiles and prints 0. Counter backup = current; does not create a second object: backup is a second reference to the very same one. By resetting current.Value, you reset the "copy" as well.

To keep the old value, copy the number itself: int saved = current.Value;. The type int is a value type, so saved does not depend on the object.

Key points

  • A variable of a value type holds the value itself, and assignment copies it whole.
  • A variable of a reference type holds a reference: assignment copies the reference, and the object becomes shared.
  • Value types are the numbers, bool, char and struct types; reference types are arrays, string and classes.
  • The stack and the heap are a simplified picture: a value type is stored where the variable is declared.