Reference types vs value types

All C# types fall into the following categories:

  • Value types
  • Reference types
  • Generic type parameters
  • Pointer types

Value types comprise most built-in types (specifically, all numeric types, the char type, and the bool type) as well as custom struct and enum types.

Reference types comprise all class, array, delegate, and interface types. (This includes the predefined string type.) The fundamental difference between value types and reference types is how they are handled in memory.

Value types
The content of a value type variable or constant is simply a value. For example, the content of the built-in value type, int, is 32 bits of data. You can define a custom value type with the struct keyword.

The assignment of a value-type instance always copies the instance.

Reference types
A reference type is more complex than a value type, having two parts: an object and the reference to that object. The content of a reference-type variable or constant is a reference to an object that contains the value.

Assigning a reference-type variable copies the reference, not the object instance. This allows multiple variables to refer to the same object — something not ordinarily possible with value types.

A reference can be assigned the literal null, indicating that the reference points to null. Calling a method or a property on a null reference will result in a NullReference exception. In contrast value types cannot have a  null assigned to them.

Storage overhead
Value-type instances occupy precisely the memory required to store their fields. Note Technically, the CLR positions fields within the type at an address that’s a multiple of the fields’ size (up to a maximum of eight bytes). Thus, the following actually consumes 16 bytes of memory (with the seven bytes following the first field “wasted”): struct A { byte b; long l; }

Reference types require separate allocations of memory for the reference and object. The object consumes as many bytes as its fields, plus additional administrative overhead. The precise overhead is intrinsically private to the implementation of the .NET runtime, but at minimum the overhead is eight bytes, used to store a key to the object’s type, as well as temporary information such as its lock state for multithreading and a flag to indicate whether it has been fixed from movement by the garbage collector. Each reference to an object requires an extra four or eight bytes, depending on whether the .NET runtime is running on a 32- or 64-bit platform.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Blog at

Up ↑

%d bloggers like this: