Value and Reference Types

Unlike some programming languages you might be familiar with, C# has two varieties of data types: value and reference. It's important to know the difference if performance is important to your application, or if you are interested in how C# manages data and memory.

When a variable is declared using one of the basic, built-in data types or a user defined structure, it is a value type. An exception is the string data type, which is a reference type.

A value type stores its contents in memory allocated on the stack. For example, in this case the value 42 is stored in an area of memory called the stack.:

int x = 42;

When the variable x goes out of scope because the method in which it was defined has finished executing, the value is discarded from the stack.

Using the stack is efficient, but the limited lifetime of value types makes them less suited for sharing data between different classes.

In contrast, a reference type, such as an instance of a class or an array, is allocated in a different area of memory called the heap. In the example below, the space required for the ten integers that make up the array is allocated on the heap.

int[] numbers = new int[10];

This memory isn't returned to the heap when a method finishes; it's only reclaimed when C#'s garbage collection system determines it is no longer needed. There is a greater overhead in declaring reference types, but they have the advantage of being accessible from other classes.

Boxing and Unboxing

Boxing is name given to the process whereby a value type is converted into a reference type. When you box a variable, you are creating a reference variable that points to a new copy on the heap. The reference variable is an object, and therefore can use all the methods that every object inherits, such as, ToString(). This is what happens in the following code:

int i = 67;                              // i is a value type 
object o = i;                            // i is boxed
System.Console.WriteLine(i.ToString());  // i is boxed

You will encounter unboxing when you use classes designed for use with objects: for example, using an ArrayList to store integers. When you store an integer in the ArrayList, it's boxed. When you retrieve an integer, it must be unboxed.

System.Collections.ArrayList list = 
    new System.Collections.ArrayList();  // list is a reference type 
int n = 67;                              // n is a value type
list.Add(n);                             // n is boxed
n = (int)list[0];                        // list[0] is unboxed

Performance issues

Let's dig a little deeper. When data is passed into methods as value type parameters, a copy of each parameter is created on the stack. Clearly, if the parameter in question is a large data type, such as a user-defined structure with many elements, or the method is executed many times, this may have an impact on performance.

In these situations it may be preferable to pass a reference to the type, using the ref keyword. This is the C# equivalent of the C++ technique of passing a pointer to a variable into a function. As with the C++ version, the method has the ability to change the contents of the variable, which may not always be safe. The programmer needs to decide on the trade-off between security and performance.

int AddTen(int number)  // parameter is passed by value
{
    return number + 10;
}
void AddTen(ref int number)  // parameter is passed by reference
{
    number += 10;
}

The out keyword is similar to the ref keyword, but it tells the compiler that the method must assign a value to the parameter, or a compilation error will occur.

void SetToTen(out int number)
{
    // If this line is not present, the code will not compile.
    number = 10;
}

See Also

Concepts

C# Language Primer

Built-in Data Types

Arrays and Collections

Reference

Value Types (C# Reference)

Reference Types (C# Reference)

Boxing and Unboxing (C# Programming Guide)