Pages

25.4.12

Reference Type vs. Value Type in C#

Types in .NET Framework are either treated by Reference or by Value. Reference types variables are used by a reference (or a pointer) that points to their data. On the other hand, Value types variables are accessed directly through their values.

There are several differences between these 2 kinds of data types. I'll try here to highlight some of them throughout examples.

Reference Type variables are stored in the heap while Value Type variables are stored in the stack. This means that when a reference type variable is no longer used, it can be a candidate for garbage collection. However, a value type variable is removed from the memory once the method using it is over.

Reference types are typically declared using "class" keyword, while the value types are declared by the keyword "struct". Let's see an example of each.

    public class ReferenceType
    {
        public int Field { get; set; }        
    }  

    public struct ValueType
    {
        public int Field { get; set; }
    }

Let's now create a console application that uses the previous types.

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Reference Type");
            ReferenceType referenceType1 = new ReferenceType();
            ReferenceType referenceType2 = new ReferenceType();
            Console.WriteLine(referenceType1.Equals(referenceType2));
            
            Console.WriteLine("Value Type");
            ValueType valueType1 = new ValueType();
            ValueType valueType2 = new ValueType();
            Console.WriteLine(valueType1.Equals(valueType2));

            Console.ReadLine();
        }
   }


You might have expected that the first Console.WriteLine statement would print "true", but the actual result is "false". System.Object is considered the parent type of all other types and it has the method Equals and implements it in a way that doesn't care about the object's fields, but rather on its reference. However, all value types inherit from System.ValueType, which itself inherit from System.Object. System.ValueType overrides the implementation of Equals with a more logical one that depends on the values of object's fields.

Let's now have another example and see how reference types are considered equal.
 
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Reference Type");
            ReferenceType referenceType1 = new ReferenceType();
            ReferenceType referenceType2 = referenceType1;
            Console.WriteLine(referenceType1.Equals(referenceType2));

            Console.WriteLine("Value Type");
            ValueType valueType1 = new ValueType();
            ValueType valueType2 = valueType1;
            Console.WriteLine(valueType1.Equals(valueType2));

            Console.ReadLine();
        }
   }


Here, both of them print "true". The second Console.WriteLine statement obviously prints true because both objects have the save values for their fields. For the first statement, this explains to us the fact that reference types are used by their reference. In other words, when assigning "referenceType1" to "referenceType2", the CLR only copies referenceType1's reference and assigns it to referenceType2 so both of them become pointing to the same object data in the memory. Let's try the following to better illustrate the idea.

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Reference Type");
            ReferenceType referenceType1 = new ReferenceType();
            ReferenceType referenceType2 = referenceType1;

            Console.WriteLine(referenceType1.Field);

            referenceType2.Field = 5;

            Console.WriteLine(referenceType1.Field);


            Console.ReadLine();
        }
   }
Now you can see that although we didn't directly change the value of referenceType1, it has been changed because it points to the same data that referenceType2 points to. The following example illustrate the idea even more.

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Reference Type");
            ReferenceType referenceType = new ReferenceType();
            referenceType.Field = 1;
            Console.WriteLine("Before: " + referenceType.Field);
            Pass(referenceType);
            Console.WriteLine("After: " + referenceType.Field);

            Console.WriteLine("");
            Console.WriteLine("Value Type");
            ValueType valueType = new ValueType();
            valueType.Field = 1;
            Console.WriteLine("Before: " + valueType.Field);
            Pass(valueType);
            Console.WriteLine("After: " + valueType.Field);

            Console.WriteLine("");
            Console.WriteLine("Value Type By Reference");
            ValueType valueType2 = new ValueType();
            valueType2.Field = 1;
            Console.WriteLine("Before: " + valueType2.Field);
            Pass(ref valueType2);
            Console.WriteLine("After: " + valueType2.Field);


            Console.ReadLine();
        }

        static void Pass(ReferenceType referenceType)
        {
            referenceType.Field = 2;
        }



        static void Pass(ValueType valueType)
        {
            valueType.Field = 2;
        }


        static void Pass(ref ValueType valueType)
        {
            valueType.Field = 2;
        }

   }


Let's move now to another aspect of difference. Add the following 2 lines to the first example and then try to compile.
    
    Console.WriteLine(referenceType1 == referenceType2);
    Console.WriteLine(valueType1 == valueType2);

These 2 lines will not compile. You will get the compilation error message : "Operator '==' cannot be applied to operands of type 'MyNameSpace.ValueType' and 'MyNameSpace.ValueType".

Let's summarize and add some other differences in the following table.
Value Type Reference Type
Where are they stored in memory? Stack Heap
Parent Type System.ValueType System.Object
Inheritance All value types are sealed and you cannot inherit from other types You can normally inherit from other types
Access modifiers protected cannot be used Any access modifier can be used

Promotional Code for Udemy ServiceNow CIS - HR Practice Tests

If you're planning to become ServiceNow Certified Implementation Specialist - Human Resources (CIS-HR), you can prepare for the exam usi...