Don’t Overlook the Basics: Understanding Type in C#
Source: Dev.to
Introduction
Understanding types is one of the most important basics in C#. Every type, no matter what, is ultimately connected to a “big boss” type called System.Object. This means that, behind the scenes, all types share some common features.
Example
// This class automatically inherits from System.Object
class Music { }
// This is the same as above, just written out
class Opera : System.Object { }
No matter what you create, it all starts with System.Object. This gives you useful methods like ToString() and GetHashCode() for every object.
Types in C#: The Basics
In C#, types are grouped into two main categories:
- Value Types
- Reference Types
There’s also a special group called primitive types, which are the most basic built‑in types.
Let’s break these down!
Primitive Types
Primitive types are the simplest types built into C#. They are easy for the compiler to use and map directly to types in the .NET framework.
Examples: int, double, bool, char, byte, short, long, float, decimal
- Primitive types are a subset of value types.
- They have default values (
intis0,boolisfalse, etc.). - They are fast and efficient.
int count = 5; // int is a primitive type
bool isOn = true; // bool is a primitive type
Value Types
Value types hold their actual data. They’re stored in a quick‑access area called the stack. When you assign a value type to another variable, it copies the value.
Examples: All primitive types, struct, enum
- Value types always have a value, even if you don’t set one.
- They can’t be
null(unless you use a nullable type). - Assignment copies the value—not just a reference.
All value types (structs, enums, primitive types) inherit from System.ValueType.
int number = 10; // Value type, default is 0 if not set
int copy = number; // Copies the value (10)
Why Are Value Types Sealed?
- Immutability of layout: Their memory layout is fixed; inheritance would make size and structure unpredictable.
- Performance: The sealed nature lets the runtime optimize storage and access.
- Simplicity: Inheriting value types would complicate assignment, boxing, and unboxing.
The following code will not compile:
struct BasePoint { public int X, Y; }
// Error! Cannot derive from a sealed type
struct DerivedPoint : BasePoint { public int Z; }
You also cannot use a value type as a base for a reference type:
struct BaseStruct { }
// Error! Cannot derive from a value type
class MyClass : BaseStruct { }
How to Create Your Own Value Type
Using struct
A struct is a user‑defined value type. Here’s a simple 2‑D point:
public struct Point
{
public int X;
public int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
}
Point a = new Point(1, 2);
Point b = a; // Copies the value (not a reference)
b.X = 10;
// a.X is still 1, b.X is 10
Using enum
All enumerations inherit from the abstract System.Enum type, which itself derives from System.ValueType. An enum is another kind of value type you can create:
public enum DayOfWeek
{
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
}
structs andenums are the only ways to create custom value types.- Value types are stored on the stack (or inline in objects) and behave differently from reference types (classes).
- You cannot inherit from a
structorenum, nor can they inherit from anything exceptSystem.ValueType.
Nullable Value Types
Sometimes you want a value type to be able to hold null (e.g., when the value isn’t set yet). In C#, you can do this with a nullable type:
int? score = null; // 'score' can now be null
if (score == null)
{
Console.WriteLine("No score yet!");
}
Reference Types
Reference types store a reference (address) to their data. The actual data lives on the heap, and the variable simply points to it. When you assign a reference type to another variable, it copies the reference, not the actual data.
Examples: class, string, array, delegate, interface, object
- Reference types can be
null(they may not point to any object). - Using a reference type before it’s assigned results in a
NullReferenceException. - Assignment copies the reference (address), not the data.
string name; // This is null by default
// Using 'name' before assigning a value causes an error
// Console.WriteLine(name.Length); // NullReferenceException!
How Memory Is Allocated: Value vs. Reference Types
One of the most important distinctions in C# is where the data lives:
| Aspect | Value Types | Reference Types |
|---|---|---|
| Location | Stack (or inline within another object) | Heap (managed by the garbage collector) |
| Assignment | Copies the actual data | Copies the reference (pointer) |
| Default value | Zero‑initialized (e.g., 0, false) | null |
| Nullability | Not nullable unless wrapped (int?) | Can be null |
| Inheritance | Inherit from System.ValueType (sealed) | Inherit from System.Object |
Understanding these differences helps you write more efficient, predictable, and bug‑free C# code.
Differences Between Value and Reference Types
The main difference between value and reference types is how and where they are stored in memory.
Value Types – Stored on the Stack
Value types are usually stored on the stack—a fast, simple part of memory. When you create a value‑type variable, the data lives right there in the variable.

Example
int number = 5;
int age = 20;
Each variable holds its value directly.
Reference Types – Reference and Object Locations
Reference types store the actual object data on the heap, but the reference (the variable that points to the object) can be stored in several places:
| Where the reference is declared | Where the reference lives |
|---|---|
| Local variable inside a method | Stack |
| Field of a class/struct | Inside the containing object (usually on the heap) |
static field | Special static memory area |
No matter where the reference is stored, it always points to the object data on the heap.


Example
class Pet { public string Name; }
class Person { public Pet pet; }
void Example()
{
Person p = new Person(); // 'p' is a local variable (reference stored on the stack)
p.pet = new Pet(); // 'pet' is a field (reference stored inside the Person object on the heap)
}
p(the reference to thePersonobject) lives on the stack because it is a local variable.- The
Personobject itself lives on the heap. - Inside the
Personobject, thepetfield holds a reference that points to another heap‑allocated object (Pet).
What Happens When You Copy?
Value‑Type Copy
int a = 10;
int b = a; // b gets its own copy of the value (10)

Reference‑Type Copy
Person p1 = new Person();
Person p2 = p1; // p2 points to the same object as p1

Both p1 and p2 refer to the same object in memory. Changing the object through either variable is reflected when accessed through the other.
Summary
- Primitive types (
int,bool, etc.) are built‑in value types. - Value types hold the actual data and live on the stack.
- Reference types hold a reference (address). The reference may be stored on the stack, heap, or in a static area, but the object data always lives on the heap.
- Assigning a value type copies the data; assigning a reference type copies the address.
- All types in C# ultimately derive from
System.Object.
Extra Tips for Beginners
- A
NullReferenceExceptionmeans you tried to use a reference type that hasn’t been instantiated. - Use nullable value types (
int?,double?, …) when you need a value type that can also benull. - Passing a value type to a method copies its value; passing a reference type copies the reference.
Understanding these basics will help you write safer and more efficient C# code. Happy coding!