Value and Reference Types


Experienced developers generally consider integers, characters, Booleans, and strings to be the basic building blocks of any language. In .NET, all objects share a logical inheritance from the base Object class. One of the advantages of this common heritage is the ability to rely on certain common methods of every variable. Another is that this allows all of .NET to build on a common type system. Visual Basic builds on the common type system shared across .NET languages.

Because all datatypes are based on the core Object class, every variable you dimension can be assured of having a set of common characteristics. However, this logical inheritance does not require a common physical implementation for all variables. This might seem like a conflicting set of statements, but .NET has a base type of Object and then allows simple structures to inherit from this base class. While everything in .NET is based on the Object class, under the covers .NET has two major variable types: value and reference.

For example, what most programmers see as some of the basic underlying types, such as Integer, Long, Character, and even Byte, are not implemented as classes. Thus, on the one hand, all types inherit from the Object class, and on the other hand, there are two core types. This is important, as you’ll see when we discuss boxing and the cost of transitioning between value types and reference types.

The key is that every type, whether it is a built-in structure such as an integer or string, or a custom class such as WroxEmployee, does in fact inherit from the Object class. The difference between value and reference types is an underlying implementation difference:

  • Value types represent simple data storage located on the stack. The stack is used for items of a known size, so items on the stack can be retrieved faster than those on the managed heap.

  • Reference types are based on complex classes with implementation inheritance from their parent classes, and custom storage on the managed heap. The managed heap is optimized to support dynamic allocation of different size objects.

Notice that the two implementations are stored in different portions of memory. The result is that value and reference types are treated differently within assignment statements, and their memory management is handled differently. It is important to understand how these differences affect the software you will write in Visual Basic. Understanding the foundations of how data is manipulated in the .NET Framework will enable you to build more reliable and better-performing applications.

Consider the difference between the stack and the heap. The stack is a comparatively small memory area in which processes and threads store data of fixed size. An integer or decimal value needs the same number of bytes to store data, regardless of the actual value. This means that the location of such variables on the stack can be efficiently determined. (When a process needs to retrieve a variable, it has to search the stack. If the stack contained variables that had dynamic memory sizes, such a search could take a long time.)

Reference types do not have a fixed size - a string can vary in size from two bytes to nearly all the memory available on a system. The dynamic size of reference types means that the data they contain is stored on the heap, rather than the stack. However, the address of the reference type (that is, the location of the data on the heap) does have a fixed size, and thus can be (and in fact is) stored on the stack. By storing a reference only to a custom allocation on the stack, the program as a whole runs much more quickly, as the process can rapidly locate the data associated with a variable.

Storing the data contained in fixed and dynamically sized variables in different places results in differences in the way variables behave. Rather than limit this discussion to the most basic of types in .NET, this difference can be illustrated by comparing the behavior of the System.Drawing.Point structure (a value type) and the System.Text.StringBuilder class (a reference type).

The Point structure is used as part of the .NET graphics library, which is part of the System.Drawing namespace. The StringBuilder class is part of the System.Text namespace and is used to improve performance when you’re editing strings.

First, here is an example of how the System.Drawing.Point structure is used:

  Dim ptX As New System.Drawing.Point(10, 20) Dim ptY As New System.Drawing.Point ptY = ptX ptX.X = 200 Console.WriteLine(ptY.ToString()) 

The output from this operation will be {X = 10, Y = 20}, which seems logical. When the code copies ptX into ptY, the data contained in ptX is copied into the location on the stack associated with ptY. Later, when the value of ptX changes, only the memory on the stack associated with ptX is altered. Altering the value of ptX had no effect on ptY. This is not the case with reference types. Consider the following code, which uses the System.Text.StringBuilder class:

  Dim objX As New System.Text.StringBuilder("Hello World") Dim objY As System.Text.StringBuilder objY = objX objX.Replace("World", "Test") Console.WriteLine(objY.ToString()) 

The output from this operation will be “Hello Test,” not “Hello World.” The previous example using points demonstrated that when one value type is assigned to another, the data stored on the stack is copied. Similarly, this example demonstrates that when objY is assigned to objX, the data associated with objX on the stack is copied to the data associated with objY on the stack. However, what is copied in this case isn’t the actual data, but rather the address on the managed heap where the data is actually located. This means that objY and objX now reference the same data. When the data on the heap is changed, the data associated with every variable that holds a reference to that memory is changed. This is the default behavior of reference types, and is known as a shallow copy. Later in this chapter, you’ll see how this behavior has been overridden for strings (which perform a deep copy).

The differences between value types and reference types go beyond how they behave when copied, and later in this chapter you’ll encounter some of the other features provided by objects. First, though, let’s take a closer look at some of the most commonly used value types and learn how .NET works with them.

Primitive Types

Visual Basic, in common with other development languages, has a group of elements such as integers and strings that are termed primitive types. These primitive types are identified by keywords such as String, Long, and Integer, which are aliases for types defined by the .NET class library. This means that the line

  Dim i As Long 

is equivalent to the line

  Dim i As System.Int64 

The reason why these two different declarations are available has to do with long-term planning for your application. In most cases (as was the case when Visual Basic transitioned to .NET), you want to use the Short, Integer, and Long designations. When Visual Basic moved to .NET, the Integer type went from 16 bits to 32 bits. Code written with this Integer type would automatically use the larger value if you rewrote the code in .NET. Interestingly enough, however, the Visual Basic Migration Wizard actually recast Visual Basic 6 Integer values to Visual Basic .NET Short values.

This is the same reason why Int16, Int32, and Int64 exist. These types specify a physical implementation; therefore, if your code is someday migrated to a version of .NET that maps the Integer value to Int64, then those values defined as Integer will reflect the new larger capacity, while those declared as Int32 will not. This could be important if your code was manipulating part of an interface where changing the physical size of the value could break the interface.

The following table lists the primitive types that Visual Basic 2005 defines, and the structures or classes to which they map:

Open table as spreadsheet

Primitive Type

.NET Class or Structure

Byte

System.Byte (structure)

Short

System.Int16 (structure)

Integer

System.Int32 (structure)

Long

System.Int64 (structure)

Single

System.Single (structure)

Double

System.Double (structure)

Decimal

System.Decimal (structure)

Boolean

System.Boolean (structure)

Date

System.DateTime (structure)

Char

System.Char (structure)

String

System.String (class)

Tip 

The String primitive type stands out from the other primitives. Strings are implemented as a class, not a structure. More important, strings are the one primitive type that is a reference type.

You can perform certain operations on primitive types that you can’t on other types. For example, you can assign a value to a primitive type using a literal:

  Dim i As Integer = 32 Dim str As String = "Hello" 

It’s also possible to declare a primitive type as a constant using the Const keyword, as shown here:

  Dim Const str As String = "Hello" 

The value of the variable str in the preceding line of code cannot be changed elsewhere in the application containing this code at runtime. These two simple examples illustrate the key properties of primitive types. As noted, most primitive types are, in fact, value types. The next step is to take a look at the specific behavior of some of the common value types in Visual Basic.




Professional VB 2005 with. NET 3. 0
Professional VB 2005 with .NET 3.0 (Programmer to Programmer)
ISBN: 0470124709
EAN: 2147483647
Year: 2004
Pages: 267

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net