When working with C#, one of the most important concepts to understand is the difference between value types and reference types.
If you understand this difference, concepts such as copying objects, modifying variables, method parameters, and memory behavior become much easier to reason about.
Let's understand it with a simple real-world example.
Imagine a Creative Agency
In a creative agency, two team leads, Sam and Taylor, have completely different ways of sharing design drafts with their teams.
Sam: Everyone Gets Their Own Copy
Sam likes to print a fresh copy of every design draft for each team member.
Each person gets their own independent copy.
If a designer writes notes on their copy or changes something, Sam's original draft remains unchanged.
For example:
Sam's Original Draft
โ
โโโ Copy โ Designer A
โโโ Copy โ Designer B
โโโ Copy โ Designer C
Everyone can modify their own copy without affecting anyone else's.
This is similar to how value types behave in C#.
Taylor: Everyone Shares One Document
Taylor prefers using a shared cloud document.
Instead of giving everyone a separate copy, Taylor gives the entire team access to the same document.
Shared Document
/ | \
/ | \
Designer A Designer B Designer C
If Designer A changes something, Designer B and Designer C can see the change because everyone is working with the same underlying document.
This is similar to how reference types work in C#.
What Are Value Types?
A value type stores its value directly.
When you assign one value-type variable to another, the value is copied.
Common value types include:
int
double
decimal
bool
char
struct
enum
Consider this example:
int firstNumber = 10;
int secondNumber = firstNumber;
secondNumber = 20;
Console.WriteLine(firstNumber); // 10
Console.WriteLine(secondNumber); // 20
When we write:
int secondNumber = firstNumber;
the value 10 is copied into secondNumber.
There are now two independent values.
Changing secondNumber does not change firstNumber.
You can think of it like Sam's printed documents:
firstNumber
โ
10
secondNumber
โ
10
After changing secondNumber:
firstNumber
โ
10
secondNumber
โ
20
The original value remains unchanged.
What Are Reference Types?
A reference type variable stores a reference to an object.
When you assign one reference-type variable to another, you copy the reference, not a completely independent object.
Common reference types include:
class
object
string
array
delegate
interface references
Consider this example:
public class Developer
{
public string Name { get; set; } = string.Empty;
}
Now create an object:
var developer1 = new Developer
{
Name = "Sam"
};
var developer2 = developer1;
developer2.Name = "Taylor";
Console.WriteLine(developer1.Name); // Taylor
Console.WriteLine(developer2.Name); // Taylor
Why did changing developer2.Name also change developer1.Name?
Because both variables refer to the same object.
Conceptually:
developer1 โโโโโโ
โ
โผ
โโโโโโโโโโโโโโโ
โ Developer โ
โ Name=Taylor โ
โโโโโโโโโโโโโโโ
โฒ
โ
developer2 โโโโโโ
This is similar to Taylor's shared cloud document.
There aren't two independent documents. Both variables refer to the same underlying object.
The Important Difference
The easiest way to remember the difference is:
Value type assignment copies the value. Reference type assignment copies the reference.
For example:
int a = 10;
int b = a;
Here, b gets its own copy of the value.
But:
var person1 = new Developer();
var person2 = person1;
Here, person2 points to the same object as person1.
A Side-by-Side Example
Let's compare both behaviors.
Value Type
int x = 10;
int y = x;
y = 20;
Console.WriteLine(x); // 10
Console.WriteLine(y); // 20
The variables contain independent values.
x โ 10
y โ 20
Reference Type
var user1 = new Developer
{
Name = "Sam"
};
var user2 = user1;
user2.Name = "Taylor";
Console.WriteLine(user1.Name); // Taylor
Console.WriteLine(user2.Name); // Taylor
Both variables refer to the same object.
user1 โโโ
โโโ> Developer { Name = "Taylor" }
user2 โโโ
What Happens When Passing Them to Methods?
This distinction becomes even more important when working with methods.
Consider a value type:
static void ChangeNumber(int number)
{
number = 100;
}
int value = 10;
ChangeNumber(value);
Console.WriteLine(value); // 10
The method receives a copy of the value.
Changing the parameter doesn't change the original variable.
Now consider a class:
static void ChangeName(Developer developer)
{
developer.Name = "Taylor";
}
var developer = new Developer
{
Name = "Sam"
};
ChangeName(developer);
Console.WriteLine(developer.Name); // Taylor
The method receives a copy of the reference, but both references point to the same object.
Therefore, changing the object's property is visible through the original variable.
This is an important detail:
Reference types are passed by value by default in C#.
What gets passed by value is the reference.
That means this:
static void ChangeName(Developer developer)
{
developer.Name = "Taylor";
}
does not mean the object itself is copied.
Instead, the reference is copied.
Reference Type Assignment Does Not Create a New Object
This is a common source of confusion for developers learning C#.
Consider:
var first = new Developer
{
Name = "Sam"
};
var second = first;
It is tempting to think that second contains a new Developer object.
It doesn't.
There is still only one object.
first โโโโโโ
โ
โผ
Developer
Name = Sam
โฒ
โ
second โโโโโโ
To create a separate object, you need to explicitly create or clone one.
For example:
var second = new Developer
{
Name = first.Name
};
Now the two variables refer to different objects.
Are Value Types Always Stored on the Stack?
You may often hear:
"Value types live on the stack, and reference types live on the heap."
This is a useful beginner-friendly simplification, but it is not an accurate rule.
Where a value is stored depends on the context and how the runtime represents it.
For example, a value type can be:
a local variable
a field inside an object
an element inside an array
boxed into an object
Similarly, a reference-type variable may be a local variable while the object it references is stored elsewhere.
The more accurate mental model is:
Value types represent their data directly, while reference types represent a reference to an object.
Focus on the semantics of copying and sharing rather than memorizing "stack vs heap."
What About string?
string is a reference type in C#.
However, strings are immutable.
For example:
string first = "Hello";
string second = first;
second = "World";
Console.WriteLine(first); // Hello
Console.WriteLine(second); // World
This can look like value-type behavior.
But string is still a reference type.
The important point is that you cannot modify an existing string object. Operations that appear to change a string actually produce another string.
So:
second = "World";
doesn't modify the original "Hello" string.
Instead, second is made to refer to another string.
Structs Are Value Types
You can also create your own value types using struct.
For example:
public struct Point
{
public int X { get; set; }
public int Y { get; set; }
}
Now:
var point1 = new Point
{
X = 10,
Y = 20
};
var point2 = point1;
point2.X = 100;
Console.WriteLine(point1.X); // 10
Console.WriteLine(point2.X); // 100
Because Point is a value type, assigning point1 to point2 creates an independent copy of its value.
Classes Are Reference Types
When you define a class:
public class Point
{
public int X { get; set; }
public int Y { get; set; }
}
the type is a reference type.
Now:
var point1 = new Point
{
X = 10,
Y = 20
};
var point2 = point1;
point2.X = 100;
Console.WriteLine(point1.X); // 100
Both variables refer to the same object.
A Simple Mental Model
Whenever you're unsure about the behavior, ask yourself:
Is this a value type?
Think:
"I'm making a copy of the data."
Original โ Copy
Changes to the copy don't affect the original.
Is this a reference type?
Think:
"I'm making another reference to the same object."
Reference A โโโ
โโโ> Same Object
Reference B โโโ
Changes made through one reference can be observed through the other.
Quick Comparison
Feature Value Type Reference Type
Examples int, bool, struct, enum class, string, array, object
Assignment Copies the value Copies the reference
Multiple variables Independent values Can reference the same object
Object sharing No, by normal assignment Yes
Example int x = y Person p2 = p1
Key Takeaway
Understanding value types and reference types is much easier if you remember Sam and Taylor.
Sam's approach is like a value type:
Everyone receives an independent copy.
Changing one person's copy doesn't affect the original.
Taylor's approach is like a reference type:
Everyone works with the same shared object.
A change made through one reference can be observed through another reference to that same object.
The most important rule to remember is:
Value type assignment copies the value. Reference type assignment copies the reference.
Once you understand this distinction, many C# conceptsโincluding method parameters, object mutation, structs, classes, arrays, and immutabilityโbecome much easier to understand.
Learn More
If you want to explore the concept in more depth, check out the full article on my website:
Top comments (0)