Reference Types (Classes)

A lot of the power of Visual Basic is harnessed in objects. An object is defined by its class, which describes what data, methods, and other attributes an instance of that class supports. Thousands of classes are provided in the .NET Framework class library.

When code instantiates an object from a class, the object created is a reference type. Recall that the data contained in value and reference types is stored in different locations, but this is not the only difference between them. A class (which is the typical way to refer to a reference type) has additional capabilities, such as support for protected methods and properties, enhanced event-handling capabilities, constructors, and finalizers; and it can be extended with a custom base class via inheritance. Classes can also be used to define how operators such as “=” and “+” work on an instance of the class.

The intention of this section is to introduce you to some commonly used classes, and to complement your knowledge of the common value types already covered. This section examines the features of the Object, String, DBNull, and Array classes, as well as briefly introduce the Collection classes found in the System.Collections namespace.

The Object Class

As noted earlier, the Object class is the base class for every type in .NET, both value and reference types. At its core, every variable is an object and can be treated as such.

Because the Object class is the basis of all types, you can cast any variable to an object. Reference types maintain their current reference and implementation but are treated generically. On the other hand value types must have their data taken from its current location on the stack and placed into the heap with a memory location associated with the newly created Object. This process of moving value type data is called “boxing” because you are taking the value and shipping it from one location to another. Boxing is discussed in more detail in Chapter 7.

The key addition to your understanding of Object is that if you create an implementation of ToString in your class definition, then even when an instance of your object is cast to the type Object, your custom method will still be called. The following snippet shows how to create a generic object and cast it back to its original type to reference a property on the original class.

Dim objVar as Object
        
objVar = Me
        
CType(objVar, Form).Text = "New Dialog Title Text"

The object objvar is assigned a reference to the current instance of a Visual Basic form. In order to access the Text property of the original Form class, the Object must be cast from its declared type of Object to its actual type (Form), which supports the Text property. The CType command (covered later) accepts the object as its first parameter, and the class name (without quotes) as its second parameter. In this case, the current instance variable is of type Form; and by casting this variable, the code can reference the Text property of the current form.

The String Class

Another class that plays a large role in most development projects is the String class. The String class is a special class within .NET because it is the one primitive type that is not a value type. To make String objects compatible with some of the underlying behavior in .NET, they have some interesting characteristics, the most important of which is immutability.

String()

The String class has several different constructors for those situations in which you aren't simply assigning an existing value to a new string. The term constructor is expanded upon in Chapter 4. Constructors are methods that are used to create an instance of a class. String() would be the default constructor for the String class, but the String class does not expose this constructor publicly. The following example shows some of the most common methods for creating a String. This example method does not show the end of this Sub, because it will be used for all of the string-related examples, with the output from these methods shown together. The following code snippet is the start of a method; the End Sub is shown later. The full Sub in the code download is the concatenation of this snippet with the next five snippets. You can build and test these parts sequentially (code file: MainWindow.xaml.vb).

Private Sub StringSamples()
    Dim strSample As String = "ABC"
    Dim strSample2 = "DEF"
    Dim strSample3 = New String("A"c, 20)
    Dim line = New String("-", 80)
 '

A variable is declared of type String and as a primitive is assigned the value “ABC.” The second declaration uses one of the parameterized versions of the String constructor. This constructor accepts two parameters: The first is a character and the second specifies how many times that character should be repeated in the string.

In addition to creating an instance of a string and then calling methods on your variable, the String class has several shared methods. A shared method refers to a method on a class that does not require an instance of that class. Shared methods are covered in more detail in relation to objects in Chapter 4; for the purpose of this chapter, the point is that you can reference the class String followed by a “.” and see a list of shared methods for that class. For strings, this list includes the methods described in Table 3.4.

Table 3.4 Methods Available on the Class String

Shared Method Description
Empty This is actually a property. It can be used when an empty String is required. It can be used for comparison or initialization of a String.
Compare Compares two objects of type String.
CompareOrdinal Compares two Strings, without considering the local national language or culture.
Concat Concatenates two or more Strings.
Copy Creates a new String with the same value as an instance provided.
Equals Determines whether two Strings have the same value.
IsNullorEmpty This shared method is a very efficient way of determining whether a given variable has been set to the empty string or Nothing.

Not only have creation methods been encapsulated, but other string-specific methods, such as character and substring searching, and case changes, are now available from String object instances.

The SubString Method

The instance method SubString is a powerful method when you want to break out a portion of a string. For example, if you have a string “Hello World” and want only the first word, you would take the substring of the first five characters. There are two ways to call this method. The first accepts a starting position and the number of characters to retrieve, while the second accepts the starting location. The following code shows examples of using both of these methods on an instance of a String (code file: MainWindow.xaml.vb), and the resulting output is the first pair of strings shown in Figure 3.8:

    ' Sub String
    Dim subString = "Hello World"
    TextBox1.Text = subString.Substring(0, 5) & Environment.NewLine
    TextBox1.Text &= subString.Substring(6) & Environment.NewLine
    TextBox1.Text &= line & Environment.NewLine

Figure 3.8 String manipulation examples

3.8

The PadLeft and PadRight Methods

These instance methods enable you to justify a String so that it is left- or right-justified. As with SubString, the PadLeft and PadRight methods are overloaded. The first version of these methods requires only a maximum length of the String, and then uses spaces to pad the String. The other version requires two parameters: the length of the returned String and the character that should be used to pad the original String (code file: MainWindow.xaml.vb):

    ' Pad Left & Pad Right
    Dim padString = "Padded Characters"
    TextBox1.Text &= padString.PadLeft("30") & Environment.NewLine
    TextBox1.Text &= padString.PadRight("30", "_") &
        Environment.NewLine
    TextBox1.Text &= line & Environment.NewLine

Figure 3.8 shows the same string first with the left padded with spaces, then with the right padded with underscores. Note that because the default font on this screen isn't fixed size, the spaces are compacted and the two strings do not appear as the same length.

The String.Split Method

This instance method on a string enables you to separate it into an array of components. For example, if you want to quickly find each of the different elements in a comma-delimited string, you could use the Split method to turn the string into an array of smaller strings, each of which contains one field of data. As shown in Figure 3.8, the csvString is converted to an array of three elements (code file: MainWindow.xaml.vb):

    ' String Split
    Dim csvString = "Col1, Col2, Col3"
    Dim stringArray As String() = csvString.Split(",")
    TextBox1.Text &= stringArray(0) & Environment.NewLine
    TextBox1.Text &= stringArray(1) & Environment.NewLine
    TextBox1.Text &= stringArray(2) & Environment.NewLine
    TextBox1.Text &= line & Environment.NewLine

The String Class Is Immutable

To support the default behavior that people associate with the String primitive type, the String class doesn't function in the same way like most other classes. Strings in .NET do not allow editing of their data. When a portion of a string is changed or copied, the operating system allocates a new memory location and copies the resulting string to this new location. This ensures that when a string is copied to a second variable, the new variable references its own copy.

To support this behavior in .NET, the String class is defined as an immutable class. This means that each time a change is made to the data associated with a string, a new instance is created, and the original referenced memory is released for garbage collection. Garbage collection is covered in detail in Chapter 2. You should be aware that this can become a comparatively expensive operation. However, having strings be immutable is important to ensure that the String class behaves as people expect a primitive type to behave. Additionally, when a copy of a string is made, the String class forces a new version of the data into the referenced memory. All of these immutable behaviors ensures that each instance of a string references only its own memory.

Next consider the following code (code file: MainWindow.xaml.vb):

    ' String Concatenation vs String Builder
    Dim start = Now()
    Dim strRedo = "A simple string"
    For index = 1 To 10000 'Only 10000 times for concatenation
        strRedo &= "Making a much larger string"
    Next
    ' The date processing below uses the built in capability
    ' to subtract one datetime from another to get the difference
    ' between the dates as a timespan. This is then output as a
    ' number of milliseconds.
    TextBox1.Text &= "Time to concatenate strings: " &
        (Now().Subtract(start)).TotalMilliseconds().ToString() &
        " String length: " & strRedo.Length.ToString() &
        Environment.NewLine
    TextBox1.Text &= line & Environment.NewLine

This code does not perform well. For each assignment operation on the strMyString variable, the system allocates a new memory buffer based on the size of the new string, and copies both the current value of strMyString and the new text that is to be appended. The system then frees its reference to the previous memory that must be reclaimed by the garbage collector. As this loop continues, the new memory allocation requires a larger chunk of memory. Therefore, operations such as this can take a long time.

To illustrate this, you'll note that the code captures the start time before doing the 10,000 concatenations, and then within the print statement uses the DateTime.Subtract method to get the difference. That difference is returned as an object of type Timespan, between the start time and the print time. This difference is then expressed in milliseconds (refer to Figure 3.8).

However, .NET offers an alternative in the System.Text.StringBuilder object, shown in the following snippet (code file: MainWindow.xaml.vb):

    start = Now()
    Dim strBuilder = New System.Text.StringBuilder("A simple string")
    For index = 1 To 1000000 '1 million times....
        strBuilder.Append("Making a much larger string")
    Next
    TextBox1.Text &= "Time to concatenate strings: " &
        (Now().Subtract(start)).TotalMilliseconds().ToString() &
        " String length: " & strBuilder.ToString().Length.ToString() &
        Environment.NewLine
    TextBox1.Text &= line & Environment.NewLine
End Sub

The preceding code works with strings but does not use the String class. The .NET class library contains the System.Text.StringBuilder class, which performs better when strings will be edited repeatedly. This class does not store strings in the conventional manner; it stores them as individual characters, with code in place to manage the ordering of those characters. Thus, editing or appending more characters does not involve allocating new memory for the entire string. Because the preceding code snippet does not need to reallocate the memory used for the entire string, each time another set of characters is appended it performs significantly faster.

Note that the same timing code is used in this snippet. However, for the StringBuilder, the loop executes one million times (versus ten thousand). The increase in the number of iterations was made in order to cause enough of a delay to actually show it requiring more than just one or two milliseconds to complete. Even with 100 times the number of iterations, Figure 3.8 still illustrates that this is a much more efficient use of system resources.

Ultimately, an instance of the String class is never explicitly needed, because the StringBuilder class implements the ToString method to roll up all of the characters into a string. While the concept of the StringBuilder class isn't new, because it is available as part of the Visual Basic implementation, developers no longer need to create their own string memory managers.

String Constants

If you ever have to produce output based on a string you'll quickly find yourself needing to embed certain constant values. For example, it's always useful to be able to add a carriage-return line-feed combination to trigger a new line in a message box. One way to do this is to learn the underlying ASCII codes and then embed these control characters directly into your String or StringBuilder object.

Visual Basic provides an easier solution for working with these: the Microsoft.VisualBasic.Constants class. The Constants class, which you can tell by its namespace is specific to Visual Basic, contains definitions for several standard string values that you might want to embed. The most common, of course, is Constants.VbCrLf, which represents the carriage-return line-feed combination. Feel free to explore this class for additional constants that you might need to manipulate string output.

The DBNull Class and IsDBNull Function

When working with a database, a value for a given column may be defined as Null. For a reference type this isn't a problem, as it is possible to set reference types to Nothing. However, for value types, it is necessary to determine whether a given column from the database or other source has an actual value prior to attempting to assign a potentially null value. The first way to manage this task is to leverage the DBNull class and the IsDBNull function.

This class is part of the System namespace, and you reference it as part of a comparison. The IsDBNull function accepts an object as its parameter and returns a Boolean that indicates whether the variable has been initialized. The following snippet shows two values, one a string being initialized to Nothing and the other being initialized as DBNull.Value (code file: MainWindow.xaml.vb):

Private Sub NullValues()
    Dim strNothing As String = Nothing
    Dim objectNull As Object = DBNull.Value
    TextBox1.Text = ""
    If IsDBNull(strNothing) Then
        TextBox1.Text = "But strNothing is not the same as Null."
    End If
    If System.DBNull.Value.Equals(objectNull) Then
        TextBox1.Text &= "objectNull is null." & Environment.NewLine
    End If
End Sub

The output of this code is shown in Figure 3.9. In this code, the strNothing variable is declared and initialized to Nothing. The first conditional is evaluated to False, which may seem counterintuitive, but in fact VB differentiates between a local value, which might not be assigned, and the actual DBNull value. This can be a bit misleading, because it means that you need to separately check for values which are Nothing.

Figure 3.9 The DBNull example

3.9

The second conditional references the second variable, objectNull. This value has been explicitly defined as being a DBNull.value as part of its initialization. This is similar to how a null value would be returned from the database. The second condition evaluates to True. While DBNull is available, in most cases, developers now leverage the generic Nullable class described in Chapter 7, rather than work with DBNull comparisons.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset