Value type and reference type

value type and reference type

C# categorizes the type of data into two types. That is a value type and reference type.

Analogy:

You want to share a document with your friends. You can either share a printed copy of it or a web link, e.g. Google Drive link. When you update the document then what happens to the shared document? If you shared a copy of the document then they will not be able to see the latest version of it. On the other hand, if you shared a link then they can see the updated version by directly accessing the link.

Let’s compare this analogy with value type and reference type in programming. Value type is like a copy of the document while reference type is like an online link.

Data storage for value type and reference type?

Value type generally stores data on stack memory while reference type stores reference type stores it inside heap memory. The memory address of this heap is stored in stack.

I mentioned “generally” in the above statement. This means, there are some instances when even value type lives in the heap. But let’s go with this understanding and cover these exceptional scenarios at the end.

What is stack memory?

Stack is a small chunk of memory within RAM and supports static memory allocation. Stack is LIFO (Last In First Out) memory management. When a method calls another method, and that calls another, runtime adds them to the stack. The last method will execute first and exist the stack. In this way, memory gets occupied and freed.

What is heap memory?

Heap is another chunk of memory in the RAM which supports dynamic allocation. The purpose of the heap is to store relatively bigger objects. The lifecycle of bigger objects can be unpredictable and that makes memory deallocation becomes difficult. This makes the heap slower than the stack. Heap relies on the garbage collection for freeing up the memory.

How data update affect value type and reference type?

As we have already learned, data in a value type lives on the stack, in some memory slot. When one updates the variable, data at that memory slot gets updated. When you assign one variable to the other, the value of that variable from its memory slot gets copied into the other variable in its memory slot. This means, both the variables will have data of their own, and changing one will not affect the other.

Example for value type:

static void Main(string[] args)
{
    //declare a value type
    int variable1 = 10;
    //create another value type and assign it's value of first one
    int variable2 = variable1;
    //Let's check Values, both should be 10
    Console.WriteLine("Initial values:");
    Console.WriteLine($"Value of variable1 is {variable1}");
    Console.WriteLine($"Value of variable2 is {variable2}");
    Console.WriteLine("\n");
    //change value of variable1
    variable1 = 30;
    //Let's check values now, variable1 should be 30 and variable2 should be 10
    Console.WriteLine("Values after changing variable1 to 30:");
    Console.WriteLine($"Value of variable1 is {variable1}");
    Console.WriteLine($"Value of variable2 is {variable2}");
    Console.WriteLine("\n");
    //change value of variable1
    variable2 = 40;
    //Let's check values now, variable1 should be 30 and variable2 should be 40
    Console.WriteLine("Values after changing variable2 to 40:");
    Console.WriteLine($"Value of variable1 is {variable1}");
    Console.WriteLine($"Value of variable2 is {variable2}");
    Console.WriteLine("\n");
    Console.ReadLine();
}

The output of the above program would be

Initial values:
Value of variable1 is 10
Value of variable2 is 10
Values after changing variable1 to 30:
Value of variable1 is 30
Value of variable2 is 10
Values after changing variable2 to 40:
Value of variable1 is 30
Value of variable2 is 40

This is a very basic example and I have added comments for the understanding purpose. An integer is a struct and all structs are value types. I have created “variable1”. As it is a value type, .Net will allocate the memory for it inside the stack, say at address #000. Now this memory address will hold the value “10”. Similarly, memory will be allocated for “variable2” at address, say #111, and “10” will be copied there. That means both memory addresses will have their own copies (similar to a friend that has a printed copy of the document). Changing the value of “variable1” won’t affect “variable2” since they have now relationship now, as you can see in the above example.

Example for reference type:

We will create a class because classes are reference types.

class MyClass
{
    public string MyProperty { get; set; }
}

Now create variables of it and play with them

static void Main(string[] args)
{
    //declare a reference type
    MyClass variable1 = new MyClass();
    variable1.MyProperty = "some string1";
    //create another reference type and assign it's value of first one
    MyClass variable2 = variable1;
    //Let's check values, both should be "some string1"
    Console.WriteLine("Initial values:");
    Console.WriteLine($"Value of variable1.MyProperty is {variable1.MyProperty}");
    Console.WriteLine($"Value of variable2.MyProperty is {variable2.MyProperty}");
    Console.WriteLine("\n");
    //change value of variable1
    variable1.MyProperty = "some string2";
    //Let's check values, both should be "some string2"
    Console.WriteLine("Values after changing variable1.MyProperty to some string2:");
    Console.WriteLine($"Value of variable1.MyProperty is {variable1.MyProperty}");
    Console.WriteLine($"Value of variable2.MyProperty is {variable2.MyProperty}");
    Console.WriteLine("\n");
    Console.ReadLine();
}

In the above example, I have created “variable1”, as soon as I declare it, .Net allocates memory for this in the stack at address, say #000. This address will hold the reference to the actual value of “variable1”, which is initially null. When I initialize it using the “new” keyword, .Net allocates another memory at address, say #999 in the heap. This will hold the actual object.

One initialized reference type has two memories, one in the stack to hold the reference of another, which is in heap.

So, now we have two reference type variables, both will have their own address in the stack. I have assigned the “variable1” to “variable2”, .Net will copy the reference from the first variable’s memory address (from the stack) to another, which means, both will point to the same reference. So when I change one variable, it will actually change the object in the heap and since both the variables are pointing to the same memory, both will have updated data (like a friend who has the online link of the document). So, the output of the above program will be

Initial values:
Value of variable1.MyProperty is some string1
Value of variable2.MyProperty is some string1
Values after changing variable1.MyProperty to some string2:
Value of variable1.MyProperty is some string2
Value of variable2.MyProperty is some string2

Exceptional case:

Let’s go through the code first and guess the output.

static void Main(string[] args)
{
    //declare a reference type
    string variable1 = "some string1";
    //create another reference type and assign it the value of first one
    string variable2 = variable1;
    //change value of variable1
    variable1 = "some string2";
    //Let's check values now
    Console.WriteLine("Values after changing variable1 to some string2:");
    Console.WriteLine($"Value of variable1 is {variable1}");
    Console.WriteLine($"Value of variable2 is {variable2}");
    Console.ReadLine();
}

In this example, I have created 2 variables, “variable1” and “variable2”. I initialized “variable1” with the value “some string1” and copied it into “variable2”. Now I changed the value of “variable1”.

Here is the interesting thing. The string is the class (not struct) that makes it reference type. As we know, changing the value of reference type will be reflected in all the variables that are referencing it. By this logic, both variables should have updated values. But when you run this, you will get different values for both of them. See the output.

Values after changing variable1 to some string2:
Value of variable1 is some string2
Value of variable2 is some string1

But why? It is because strings are immutable. Immutable means, you can not change them. When you try to change it, it will create another object. In this example, when I tried to change “variable1” to “some string2”, a new object was created behind the scene and “variable1” started referencing it while “variable2” was still referencing the old one.

Which one is better?

The other way of asking this question is should one create a value type (struct) or a reference type (class). The answer is not very straightforward. Let’s go back and remember how these two stores data. Value type lives inside the stack. Stack is faster than heap but has a small amount of memory. So when there are very few properties simple methods, go with a value type.

When you need to encapsulate a medium to a high number of properties, go with the reference type.

Personally, I create reference types most of the time due to various reasons. First, a class may depend on other classes which makes them heavier. Second, even if properties look limited at the time of implementation but they can grow later and logic can also become more complex. Third, struct doesn’t support features like inheritance, polymorphism, etc.

When a value type is stored in the heap?

If you remember, we learned that not all value types are stored at stack. Below are such scenarios.

  1. Value type which is directly declared inside a class is stored in heap.
  2. Static variables.
  3. Variables used inside lambda expression.
  4. Variables inside the iterator blocks.
  5. Parameters that are passed as reference.

I hope this was helpful. Happy coding

CategoriesC#

Leave a Reply

Your email address will not be published.

Coding Crest Back To The Top