3.6 Value Types

Reference types and value types are both derived from System.Object . However, value types are treated differently in terms of allocation. When a value type is passed to a ByVal method, the actual value is copied. Also, assigning a value type to another instance of that type causes the value to be copied . The exception occurs when a value type is a reference type's data member; then it is heap allocated right along with the rest of the class.

In the following code fragment, number1 and number2 are two distinct locations in memory that each contain the value 5 :

Dim number1 As Integer = 5 Dim number2 As Integer = number1

This contrasts with reference types (classes). Consider the following fragment, in which two objects are instantiated and then one is assigned to the other:

After the assignment, there are no longer two objects; object2 is now a reference to object1 . The location in memory formerly associated with object2 is now sitting around waiting to be garbage collected.

Sometimes defining a value type is beneficialespecially when you have small amounts of related data (that consist of value types). A perfect example is the Point structure:

Public Structure Point Public x As Integer Public y As Integer End Structure

Value types always contain known values. The compiler automatically generates a default constructor that is used to initialize all the values of the structure to a known value.

Once a Point is declared, it can be used just like any other value type. There is no need to use the New operator because the default constructor is called automatically:

'After assignment p1 and p2 are two different points Dim p1 As Point Dim p2 As Point p1.x = 10 p1.y = 10 p2 = p1

Structures have many of the same features as classeslike constructors, for instance. While the use of a default constructor is restricted (no arguments), it is legal to define a constructor that contains parameters:

Public Structure Point Dim x As Integer Dim y As Integer Public Sub New (x As Integer, y As Integer) Me.x = x Me.y = y End Sub End Structure

However, the New operator must be used to initialize a structure through a nondefault constructor:

'Still have two different points Dim p1 As New Point(10,20) Dim p2 = p1

Structures have other similarities to classes (they can have methods and events), but there are other limitations besides not being able to define a default constructor:

Structures implicitly inherit from System.ValueType , but the buck stops there. It is not possible to inherit from a structure.

Finalize methods are not allowed.

Protected members cannot be used.

It is impossible to use the New keyword to initialize member data at the time of declaration.

Array sizes cannot be specified at the time of declaration.

Structures are best used to group small pieces of related data that do not require additional functionality. It's recommended that the instance size of a structure does not exceed 16 bytes. The Point structure is a perfect example that meets both of these criteria.

The data members of Point are value types, which means that both x and y are allocated inline within the structure. If the structure contained a reference object like a String , the String would be allocated on the heap just like any other reference type. However, the reference to that String object would be allocated inline in the structure. If the String object in question were a member of another class, then both the object and the reference would be heap allocated. Thus, even if a structure contains a reference type member, theoretically, there is one less level of indirection. Whether there is a noticeable difference (performance-wise) in a practical situation remains to be seen.

Ideally, a structure should contain only value type members so that everything is stack-allocated. This situation works especially well when you deal with arrays:

Public Sub LotsOfPoints( ) Dim points(1000) As Point 'Use points End Sub

Here, the overhead of calling the constructor is avoided and, likewise, the garbage collector does not have to free 1,000 objects. The stack is destroyed when the method returns.

Value types can be used where an object is expected. For example, Console.WriteLine expects an Object as a parameter, but it easily accommodates a Point :

Dim p As New Point(11,6) Console.WriteLine(p)

When a value type is used where an object is expected, an object wrapper is allocated on the heap, and the actual value is copied into it. This wrapper makes it appear as if the value type is a reference type. This process is called boxing , and you should obtain a minimal understanding of the process to avoid potential performance problems. Boxing operations aren't cheap.

The various collection classes in the framework expect objects. Adding 1,000 Points to a collection could result in 1,000 boxing operations, not to mention the fact that 1,000 objects are now in line for garbage collection.