Value Type:
Built in types such as int(all numeric types),char,bool etc.. would come under value type. Custom struct and enum would also fall into this category. Value types exhibit following characteristics.
- Actual Value(s) gets copied when a variable is assigned to another variable and there is no relationship between two variables after the assignment statement
Consider the following code block
int i = 2; int j = i;
The statement j=i does two things – creating a new variable by name j of type int and assigning the value of i (2 in this case) to the variable j.
j=5;
In the above statement, the value of variable j is changed to 5. Changing the value of j will not have any impact on the value of i.
Below is the pictorial representation of the same.
2. There is one to one mapping between variable name and actual object value it refers to.
Let’s see an example with Struct which is also a value type.
struct Rectangle { public int length; public int width; }
In the below code, a new instance of struct Rectangle by name rec1 is created and length,width values are assigned respectively
Rectangle rec1 = new Rectangle(); rec1.length = 10; rec1.width = 20;
The below statement creates new instance by name rec2 and copies all the fields with values from rec1 to rec2.
Rectangle rec2 = rec1;
Now there are 2 instances of Rectangle – rec1 and rec2. The Rectangle instance created using the first code block can only be accessed using the variable rec1 and the rectangle instance created using the code statement(Rectangle rec2 = rec1;) can only be accessed using the variable rec2.
If you want to update a field in the instance, you can update by accessing the instance variable name.
For eg, rec2.width =25 will change the width field of rec2.
Below is the pictorial representation.
3. Value types are short-lived and they can be stored in stack. But it’s not the case always…
class Program { static void Main(string[] args) { int i = 3; IncrementAndPrint(i); i=4; } static void IncrementAndPrint(int i) { int j = i + 1; Console.WriteLine("j:" + j); } }
Below is the sequence of steps happening at the above program with respect to value types. Please note that this is very high level approximation and is explained for the purpose of understanding of life of value type.
- While entering the Main method of the program, stack is empty
- In the first line of the Main method, a variable by name i is created and assigned a value 3. This variable gets added to the stack.
- IncrementAndPrint method is called with variable i as parameter. So this value is copied to variable i(scope local to the IncrementAndPrint method). This variable is also stored in stack. Now the stack contains 2 items.
- A new variable by name j is declared and stored the value with 4( i+1). Now the stack contains 3 items.
- It prints the value of j in next line and exits the method. Now all the variables which were in scope of IncrementAndPrint method would be removed from the stack. i.,e variable i (local to IncrementAndPrint method), j .
- Now the stack would contain only 1 item which is variable “i” local to the Main method.
- Thus value types are added and removed from the stack as they enter and leave the code block.
Below is the pictorial representation of the above discussion
Important Note: Though value types have the ability to be stored in stack, it’s not necessary that value types are always stored in stack. Sometimes values types are in heap(block of memory) if they have to persist for a long period of time. E,g static variables, value types wrapped inside reference type.
Reference Type:
Reference types has 2 parts – object and reference part. Class, Arrays would fall into the Reference type category and exhibits the below characteristics
- Only reference gets copied when a reference type variable is assigned to another variable and there is a relationship maintained between two variables after the assignment statement.
Let’s see example with class (a reference type). Below class represents a point in 2D canvas with 2 fields x and y.
class Point { public int x; public int y; }
The below code creates a new instance of Point class and the reference to the created object(instance) is stored in variable P1. The field values of created object is assigned through the reference of the object.
Point P1 = new Point();
When a reference type variable is created, following things happen
1. A new instance of the reference type (instance to the Point class) is created. All the fields would be set to its default value when the instance is created. As both fields are of type int – the value 0(default value of int) would be set to those fields.
2. A reference(P1) is created
3. The reference is pointed to newly created instance.
Below is the pictorial representation
Point P2 = P1;
When the above line of code is executed, new instance of Point is NOT created – only the new reference by name P2 is created and P2 would point to the same instance that P1 is referring to.
P2.y = 20;
When I execute the above code statement, it updates the filed y of the created object – the object which both P1 and P2 points to. So when you try to print the value of value of P1.y using the below statement , it would print 20
Console.Writeline(P1.y); // Prints 20
However, this relationship is not permanent– the reference can be pointed to some other object by assigning the new object. The below code would point P2 to newly created object.
Point P3 = new Point(); P2 = P3;
2. There is “many to one mapping” between variable name and actual object value it refers to.
As we have seen earlier, P2 and P3 references points to the same object.
3. References are passed by value but reference types are not.
When a reference is passed to a method, the value of the reference is copied. Any changes to the actual object through the reference will change the state of the object but any changes to reference itself will not change either the state of the object or the earlier reference pointing to the object.
In the below Main method of the program, I am creating a new instance of the Point class and assigning the values to the fields. Method1 is called with the reference P1 as parameter.
static void Main(string[] args) { Point P1 = new Point(); P1.x = 10; P1.y = 20; Method1(P1); Console.WriteLine(P1.x); //Prints 15 Console.WriteLine(P1.y); //Prints 20 } static void Method1(Point P) { P.x = 15; Console.WriteLine(P.x); Console.WriteLine(P.y); P = null; }
In Method1, a new reference P is created. This reference would point to the same object created in Main method(as discussed in point 1 earlier) . The state of the object is changed by the below statement.
P.x = 15;
When the values of the instance members, the fields would contain updated values (15,20)
Setting the reference P to null would remove the link between reference and the actual object. This will not impact the original object.
When you are back in Main method, you can access the object through reference P1 as you were accessing earlier.
4. Reference types are relatively long-lived and they are stored in heap
When a reference type object is created, the object is created in heap and a reference is returned. Heap is a block of memory where reference objects reside (sometimes long living value objects also reside at heap). As we just saw, the actual Point object is created at heap and the reference is returned (P1 in our case). There may be many references pointing to the same object.
When Method1 was called, the reference was passed by value to the Method1.
Method1(P1);
Unlike value types, the reference types are not removed as they leave the code block. In fact, no method holds the object – only the references are passed to method so that object can be accessed through that reference. Garbage collector would clear the objects which are no longer required based on memory foot print and other factors.
Please subscribe if you find this article useful.
[mc4wp_form]
Leave a Reply