What is The Difference Between Value Types and Reference Types in C#?

Özkan ARDİL
7 min readJan 9, 2024

--

Understanding the fundamental differences between value types and reference types in C# is equivalent to mastering the fundamental building blocks of the language.

In this article we will;

  • Begin an exploration of value types and reference types in C#,
  • Investigate the intrinsic differences between them,
  • Analyzing their behavior,
  • The impact of memory allocation nuances on program execution.

By the end of this article, I aim to provide you with a comprehensive understanding of value types and reference types in C#, which will enable you to craft robust, efficient, and resilient code with precision.

In C#, we have two main types of variables: value types and reference types.

Value Types in C#

These are simple types like numbers (integers, decimals), dates, and Boolean values (like true/false). When you use a value type, the actual data is stored directly where you define it. So, when you pass a value type as an argument to a method or assign it to another variable, a complete copy of the data is made.

Reference Types in C#

These include objects like strings, lists, arrays, and other custom-made classes. When you work with reference types, what’s stored in your variable is not the actual data, but a reference or a pointer to where the data is stored in memory. When you pass a reference type as an argument or assign it to another variable, you’re passing or assigning the reference, not the actual data.

Key Differences Between Value Types and Reference Types in C#

Inheritance: Value types inherit from System.ValueType, while reference types inherit from System.Object.

Passing as Parameters: When a method receives a value type as an argument, it gets a copy of the actual data. With reference types, the method receives a copy of the reference, not the data itself.

Assignment: When you assign a new value to a variable holding a value type, the whole data is copied. For reference types, only the reference gets copied.

Inheritance and Storage: All value types are sealed, meaning they cannot be inherited, while reference types can be inherited. Value types are stored on the stack, which is more direct and fast. Reference types are stored on the heap, which allows for more flexibility but requires garbage collection to clean up unused memory.

See the “What is the purpose of the “sealed” modifier?” article for more information.

The main distinction lies in how they hold data. Value types contain the actual data, while reference types hold a reference, which is like a link or an address pointing to where the data is stored in memory.

To better understand this, let’s consider two real-life scenarios:

Scenario 1: The Recipe Swap

Imagine hosting a delightful cooking event where I met a talented chef named Alice. After the event, I recorded Alice’s secret recipe for a delicious dish on a recipe card. At a later time, when we met, I expressed my excitement about meeting Alice, and you inquired about the recipe. You wrote it down on your own recipe card. Here, Alice’s recipe is similar to a value type. You made a duplicate of the exact information. If I decide to modify the recipe on my card, it won’t affect the one you have. Each recipe card, similar to a variable, holds its own unique information.

Scenario 2: The Art Exhibition

Now, imagine visiting a stunning art gallery where I discovered a breathtaking painting by an artist named Sophia. I was given a map by the gallery staff that showed the location of Sophia’s masterpiece. As I admired the artwork, I met you and passionately described this magnificent painting. You noted down the gallery room’s address. In this comparison, the painting symbolizes a reference type. I can only give you the address, just like a reference in C#. If you went to the gallery and drew on the painting (but please don’t!), it would change the painting I liked because it’s the only one there. There’s only one original in the gallery, but multiple references can point to it.

Now, let’s look closer and more technically at the differences between value and reference types.

Example for Value Type as a Parameter

As you can see we defined one method. It alters the parameter that was passed to it.

 internal static class Caculator
{
public static void AddOne(int number)
{
++number;
}
}

We call the method within program.cs class.

Console.WriteLine("Value type as parameter. \n");

int a = 10;
Console.WriteLine($"a variable is {a} \n");

Caculator.AddOne(a);
Console.WriteLine($"Now, new a variable is {a} \n");

Console.ReadLine();

What do you think this code will print?

Result when a value type is passed as a parameter in c#

Well, it will print “10” twice! This is because the a variable has been passed to the AddOne method by a copy. The AddOne method incremented the copy, so the original a variable has not been affected.

Example for Value Type When Copied

In this sample, I created a new class to show how value types behave when copied.

  public static void CopyValueTypeMethod(int number)
{
int b = number;
int c = b;
++c;

Console.WriteLine($"Variable b is {b} \n");
Console.WriteLine($"Variable c is {c} \n");
}

We call the method within program.cs class.

Console.WriteLine("Value type when copy. \n");

int a = 10;
Console.WriteLine($"a variable is {a} \n");

CopyValueType.CopyValueTypeMethod(a);
Console.WriteLine($"Now, new a variable is {a} \n");

Console.ReadLine();

This is the result printed to the console.

Result when a value type is copied in c#

Incrementing the c variable did not affect the b variable, since a copy was created at the assignment.

So, let’s take a look at the reference types.

Example for Reference Type as a Parameter

In this sample, I created a new class to show how reference types behave when it is passed as parameter.

You’ll see the ListContainer class below.

 internal static class ListContainer
{
public static void AddOneToList(List<int> list)
{
list.Add(1);
}
}

We call the method within program.cs class.

What do you think will be printed?

Console.WriteLine("Reference type sample! \n");

var list = new List<int>();
Console.WriteLine($"List contains {list.Count} elements \n");

ListContainer.AddOneToList(list);
Console.WriteLine($"Now, list contains {list.Count} elements \n");

Console.ReadLine();

The output of the code is attached below.

Result when a reference type is passed as a parameter in c#

Since the List is a reference type, the AddOneToList does not operate on its copy, but on the same object that is referenced by the list variable. Because of that, the number of elements in the list variable has been incremented by one.

Example for Reference Type When Copied

In this sample, I wrote the code directly into the program.cs to show how reference types behave when copied.

Console.WriteLine("Reference type when copied \n");

List<int> listA = new List<int> { 1, 2, 3 };
List<int> listB = listA;

listB.Add(4);

Console.WriteLine($"ListA contains {listA.Count} elements which are {string.Join(", ", listA)} ");
Console.WriteLine($"ListB contains {listB.Count} elements which are {string.Join(", ", listB)} ");

For this code, the result will be;

Result when a reference type is copied in c#

On assignment, no copy of the listA was created. Only the reference was copied, and it still points to the same object. Because of that adding an element to the listB variable, also affected listA — because listB and listA point to the same object.

You can access the source code of the project on my C# Subjects GitHub Repository account.

If you can give the repository a star and share the article, you will support me in reaching more people.

👏 If you found the content useful, I would appreciate it if you could support it with applause (you can also contribute with more than one applause by holding down the clap button for a long time).

⬇️ Check out my other articles.

💬 Let me know in the comment section what have I missed? Where I was wrong?

👨‍👦‍👦 You can also share this article with your friend. Argue a little about it. It is known that the truth is born in a dispute.

🙃 Stay determined, stay focused, and keep going.

Thanks for reading…

--

--

Özkan ARDİL

.NET C# JS and Angular dev with 8+ yrs exp, self-taught & passionate web developer. Sharing tips & experiences in C# and web dev.