Friday, September 3, 2010

Difference Between Value Types and Reference Types

In C# there are, like in many programming languages, basically 2 datatype variants: There are value types and reference types.
Variables, which base on value types, save the actual value of the variable. In contrast to that, variables which base upon reference types, only save a reference to the memory address of the variable.
Another difference between these types is: Value types are saved in the stack, reference types in the heap.
If you copy a value type, the value gets copied, after that exist 2 variables with the same value. If you copy the value of a reference type though, only the reference is copied, afterwards there are 2 references point on the same value in the heap. So if you change the value of the original reference variable, you also change the value of the copied reference variable, as they point to the same memory address.
The most datatypes in C# are value types, as for example byte, int and char. Reference types are generally only classes and arrays.

So why do we need to know all this when programming?
Most essential is this knowledge, when calling functions with parameters.
The following easy example should illustrate this:

        private void Form1_Load(object sender, EventArgs e)
            // value types
            int NumberA = 8;
            int NumberB = 3;

            // reference types
            int[] Numbers = { 8, 3 };

            int NumberC = ResultValue(NumberA, NumberB);
            int NumberD = ResultReference(Numbers);

        private int ResultValue(int numberA, int numberB)
            numberA /= 2; // divide numberA by 2
            return numberA * numberB;

        private int ResultReference(int[] numbers)
            numbers[0] /= 2; // divide first number in the array by 2
            return numbers[0] * numbers[1];

There are 2 functions, ResultValue() and ResultReference(), which are called from Form_Load().
The first expects 2 variables of the type integer as parameter, which are the numbers 8 and 3. In the first function the first given number (8) is divided by 2 and the product of both numbers (4 * 3 = 12) is returned.
In the second function ResultReference() also the first number of the array is divided by 2 and the product returned (4 * 3 = 12).
However, the integer parameters of the first method are value types, so when handing them over to ResultValue(), a copy of them is created in the memory. The division is only done with the copy, the original number NumberA still is 8 after returning from the function.
The array given to ResultReference() is a reference type, when handing the parameter over to the function only the reference is copied, the variable still points to the same memory address in the heap. If the first number of the array is divided by 2, after returning from the function also the original number in the array is 4.

To change this behaviour, there is the keyword ref. If this is put in front of the value type parameter, this is created as a reference type parameter, when handing it over to the function, no value copy is created, but the reference to the original value:

            // value types
            int NumberA = 8;
            int NumberB = 3;

            int NumberC = ResultValue(ref NumberA, ref NumberB);

In the above example then the original variable, NumberA, is divided by 2.
To convert value types to reference types there is no corresponding keyword, when handing over the parameters manually a true copy of the object, a deep copy, has to be created.

No comments:

Post a Comment