|Deep C# - Value And Reference|
|Written by Mike James|
|Thursday, 27 August 2015|
Page 3 of 3
If you think you follow try predicting the result of (i.e. predict the values in each of the fields) the admittedly complicated:
The answer is:
because d is the same object as b but c is a new object initialised to be the same as a.
The difference between the two types of behaviour is usually expressed as value semantics versus reference semantics, i.e. is the variable the value or a reference to the value.
The advice is that structs should be used for simple data types where value assignment seems natural and classes should be used for more advanced types where reference assignment is natural.
The “value” semantics also has another spin-off when it comes to arrays and indeed other complex data types.
When you create an array you always use something closer to reference-type semantics as in:
This creates 10 ints in a contiguous block of memory and this is very efficient.
This creates a contiguous block of 10 reference variables of type PointR all referencing - nothing at all. That is all you have created is an array of references to null.
To finish the array construction you have to use something like:
This is, of course, not as efficient because the 10 objects are created on the heap and have to be memory managed, but all object-oriented languages suffer from this type of problem.
The good news is that C# treats arrays created using structs in the same way as simple types, i.e. using value semantics.
immediately creates an array of 10 PointV objects in a contiguous block of memory.
As long as you keep thinking of value types and reference types as being more or less the same except reference types don't automatically create the object they reference you should follow and things should seem simple.
Structs v classes
Structs are so important and versatile that we devote a whole chapter to them later on but it is worth saying a few words that compares and contrasts them to classes while we are on the topic.
As you might expect of a value type structs don’t support inheritance – they inherit from object but that’s the end of the inheritance hierarchy.
They do support interfaces, however, more of which in the next chapter. Also a struct can have methods, properties and constructors, but it can’t have a destructor. Its default, i.e. parameter-less, constructor can’t be changed and always initialises all its fields to the default value of zero for a value type and to null for a reference type.
You can add your own explicit constructor to initialise fields to specific values. Surprisingly although you can create a struct without the use of “new” you can write:
It is important to realise that this way of creating a struct is no different from the point of view of value or reference semantics - a struct is always a value type. .
There is a difference, however, and it is that the use of new calls the struct’s default constructor which sets each field fof the struct to its default value. If you don’t use new then the fields are treated as undefined and the compiler displays an error message it you try to make use of any of the fields before they have been assigned to.
Structs are interesting for many reasons and, as mentioned initially we will return to examine them in more detail in a later chapter.
The simple data types, e.g. int, char, ... are clearly value types but conceptually at least they are also examples of structs.
Most of the time you can ignore this fact and just treat them as simple numbers or characters but they do have methods.
For example. int is just an alias for the System.Int32 struct which inherits from object and so it has a ToString method:
Each simple type also has methods of its own and even static methods. For example:
Of course you can’t inherit from a simple type and they have other special features such there being literals of the same type e.g. 10, 233.34 and so on.
Simple types are structs in spirit only as the compiler takes care to make sure that they are implemented in an efficient manner. Notice that as with a struct you can opt to create a simple type using the new keyword.
This doesn’t have any effect other than to call the types default constructor which results in it being initialised to its default value. So for example
results in a compiler error because j isn’t defined. However:
works perfectly because j is initialised to zero by its constructor.
Value or Reference - Decide Early Don't Change
You should by now understand the difference between a value and a reference type but when do you use one in preference to another?
In most cases this question is simply about using a class or a struct. The answer is nearly always that you should prefer a value type when the purpose is to store data and a reference type when some behaviour has to be implemented.
What ever you choose you need to be aware of the potential for trouble that any attempt at changing a value type to a reference type can product.
For example, suppose we initially design a program using a struct:
We might very well use a method to do some computation involving a private instance of this struct:
In this example no computation is performed the struct is simply returned. Now if some other part of your program uses this method it might well go on and perform further manipulations on the returned value:
In this case everything works as you would expect. The struct is treated using value semantics and the doSum returns a complete copy of the struct to create a completely separate entity in a.
This means that storing 10 in the x field doesn’t alter the value stored in MyData. Now consider the seemingly small change from a struct to a class:
The change to the private variable and the method are easy enough:
However if the client program performs the same manipulation:
the result is very different. Now the object passed as the result is treated as a reference type and subject to reference semantics. This means that doSum passes back a reference to the supposedly protected Mydata object. Now when we store 10 in the x field the its is the field in the Mydata object which is changed.
Clearly changing from a value type to a reference type and vice versa is a potentially deep structural change to a program because it changes value semantics into reference semantics.
or email your comment to: email@example.com
|Last Updated ( Wednesday, 18 November 2015 )|