developmentor - a developer services company				  
				      C# Tutorial
   Modules

Reference Types


Goals:

  • Introduce the concept of references.
  • Describe the behavior of references in declaration, assignment, and parameter passing.
  • Discuss issues surrounding null references.
  • Briefly talk about memory management and garbage collection.

Overview

Arrays and classes are reference types. This means that two things are involved when dealing with instances: a reference and the object itself. A reference is used as a handle to manipulate the object. This organization has a number of implications for how reference types behave during operations such as assignment and parameter passing.


Reference declaration

A variable declaration of a class type yields only a reference, it does not create an object of that class. References are often called "handles" or "pointers" since they are used to manipulate or point at objects.


Object creation

Objects are created using the new operator and reference variables are used to access them. To see this, it is instructive to look at some code where the two steps are done separately. In the example shown below, first a few Stock references are created and then objects are allocated using new. Notice how the assignment operator is used to make the reference variables refer to the newly created objects.

It is also possible to do both the reference declaration and object creation in a single line of code as shown below.


Reference assignment

Assigning references copies only the reference, it does not copy the data inside the object. To see this, consider the following code which shows two references each referring to a separate object.

Now consider performing an assignment such as "ibm = sun;". Because ibm and sun are both references, the assignment copies only the reference. In other words, ibm now refers to the same object as sun. The result of such an assignment is shown below. Note that the object ibm used to refer to is no longer accessible since there are no references to it. We will discuss what happens to such objects later in this module when we talk about memory management.


null

The link between a reference and an object can be broken by setting the reference to null. For example, in the code below, the reference ibm is initially referring to an object. After the assignment, the link has been severed and the reference is no longer referring to any object.

Attempting to use a null reference is an error. The error is detected at runtime by the CLR. The CLR stops the operation and throws a NullReferenceException to notify the program of what went wrong. The try/catch exception handling syntax can be used to trap and handle the exception. We will discuss exceptions in greater detail in a later module.

In order to avoid a NullReferenceException, it is common to test a reference before use. Only if the reference is not null should the operation proceed.

It is common to use a reference type as a field. For example, a parent might hold a reference to each child, a highway would need to keep references to each city it connects, and a circle must have a reference to its center point. Recall that each field gets assigned a default value based on its type: numeric fields get set to zero, Boolean fields are set to false, etc. In a similar fashion, reference fields are set to null when the object containing them is allocated. The code below shows the case for "circle with center point".


Reference parameters

When reference types are passed as parameters, only the reference is passed and the object is not copied. The figure below illustrates the situation.

Passing a reference has two important implications. First, reference parameters can be quite efficient since only a reference is passed and there is no need to copy all the data inside the object. Second, because the method holds a reference to the original object, any changes it makes modify that object. The effects of these changes will be visible to the client code after the method returns.


Array

Arrays are reference types just like class types so they are implemented as reference/object pairs. An array declaration creates only a reference. The associated array object must be created using the new operator. The figure below illustrates the two steps needed to create an array.

It is common to declare the reference and create the array in a single line of code. This style is a bit cleaner since the reference is created and then immediately bound to an array object. The figure below shows the two steps being performed together.

Because arrays are references type, they behave just like class types in operations such as assignment and parameter passing. For example, assigning one array to another copies the reference and not the data inside the array. This situation is shown below.

As a final variation on arrays, we will look at a slightly more complicated case: an array of references. Creating an array of a class type with an expression such as "Stock[] s = new Stock[3];" creates an array of Stock references with each reference initially set to null. It is important to emphasize that at this point we have only an array of references, there are no Stock objects in existence yet. As a second step, we need to create the Stock objects and use the array elements to refer to them. The situation is illustrated below.


Memory management

In the last few modules we have allocated quite a few objects using new but have not worried about where the memory comes from nor how it gets reclaimed when we have finished with the objects. Happily, there is not much for the programmer to do since C# and .NET automatically recycle the memory of unused objects. The situation is similar to what many children experience during their earliest years. During play time, the child removes a toy from the toy box and plays with it for a while. When they grow bored they simply set the toy down where they are and return to the toy box for another one. After a while, there are discarded toys strewn all around the house. At this point, a responsible adult comes along and collects all the toys and returns them to the toy box where they are available for reuse.

More technically, memory for objects of reference types comes from an area of memory called the "managed heap". The .NET heap is called "managed" since the CLR takes responsibility for managing the allocation and reclamation of the memory. Operator new allocates memory for reference types from the managed heap and the "garbage collector" reclaims the memory occupied by unused objects. An object becomes unused when it is no longer reachable from the program. For example, if the only reference to an object is a local variable, then at the end of the method the local variable goes out of scope and the reference disappears. The object is now unused and is eligible to be reclaimed by the garbage collector.

The garbage collector has quite a difficult job since it must determine which objects are still active and which objects are no longer in use by the program. To do this, the garbage collector examines all the "root" references such as live local variables and static fields. It follows these references and marks any objects it finds as being still active. After all live objects have been marked, the garbage collector assumes all other objects are no longer in use and recycles the memory they occupy. The basic idea is illustrated in the figure below.

The CLR controls the timing of garbage collection. The common advice is that the CLR and the garbage collector are intelligent enough to do the job without any interference from the programmer. However, the GC.Collect method can be used to force the garbage collector to run if it becomes necessary.

Despite the sophisticated support for memory management offered by the CLR, it is still possible for a program to allocate so many objects that it runs out of available memory. When this happens, the CLR will notify the program by throwing an OutOfMemoryException. The program can use the exception handling try/catch construct to trap and handle the exception.

This material is excerpted from the Programming C# course offered by DevelopMentor.