The last item in the code snippet in the preceding section illustrates an additional keyword associated with redimensioning. The Preserve keyword indicates that the data stored in the array prior to redimensioning should be transferred to the newly created array. If this keyword is not used, then the data stored in an array is lost. Additionally, in the preceding example, the ReDim statement actually reduces the second dimension of the array. Although this is a perfectly legal statement, this means that even though you have specified preserving the data, the data values 3, 6, 9, 12, and 15 that were assigned in the original definition of this array will be discarded. These are lost because they were assigned in the highest index of the second array. Because arrMyIntArray4(1,2) is no longer valid, the value that resided at this location (6) has been lost.
Arrays continue to be very powerful in Visual Basic, but the basic Array class is just that, basic. It provides a powerful framework, but it does not provide a lot of other features that would enable more robust logic to be built into the array. To achieve more advanced features, such as sorting and dynamic allocation, the base Array class has been inherited by the classes that make up the Collections namespace.
The Collections namespace is part of the System namespace. It provides a series of classes that implement advanced array features. While the capability to make an array of existing types is powerful, sometimes more power is needed in the array itself. The capability to inherently sort or dynamically add dissimilar objects in an array is provided by the classes of the Collections namespace. This namespace contains a specialized set of objects that can be instantiated for additional features when working with a collection of similar objects. Table 8-1 defines several of the objects that are available as part of the System.Collections namespace.
TABLE 8-1: Collection Classes class description
ArrayList Implements an array whose size increases automatically as elements are added.
BitArray Manages an array of Booleans that are stored as bit values.
Hashtable Implements a collection of values organized by key. Sorting is done based on a hash of the key.
Queue Implements a first in, first out collection.
SortedList Implements a collection of values with associated keys . The values are sorted by key and are accessible by key or index.
Stack Implements a last in, first out collection.
Each of the objects listed focuses on storing a collection of objects. This means that in addition to the special capabilities each provides, it also provides one additional capability not available to objects created based on the Array class. Because every variable in .NET is based on the Object class, it is possible to have a collection that contains elements that are defined with different types. So a collection might contain an integer as its first item, a string as its second item, and a custom Person object as its third item. There is no guarantee of the type safety that is an implicit feature of an array.
Each of the preceding collection types stores an array of objects. All classes are of type Object, so a string could be stored in the same collection with an integer. It's possible within these collection classes for the actual objects being stored to be different types. Consider the following example code within ProVB_ VS2010 download for Chapter 8:
Private Sub SampleColl()
Dim objMyArrList As New System.Collections.ArrayList()
Dim objItem As Object
Dim intLine As Integer = 1
Dim strHello As String = "Hello"
Dim objWorld As New System.Text.StringBuilder("World")
' Add an integer value to the array list. objMyArrList.Add(intLine)
' Add an instance of a string object objMyArrList.Add(strHello)
' Add a single character cast as a character. objMyArrList.Add(" "c)
' Add an object that isn't a primitive type. objMyArrList.Add(objWorld)
' To balance the string, insert a break between the line ' and the string "Hello", by inserting a string constant. objMyArrList.Insert(1, ". ")
For Each objItem In objMyArrList
' Output the values on a single line. TextBoxOutput.Text += objItem.ToString()
TextBoxOutput.Text += vbCrLf For Each objItem In objMyArrList
' Output the types, one per line.
TextBoxOutput.Text += objItem.GetType.ToString() & vbCrLf
The preceding code is an example of implementing the ArrayList collection class. The collection classes, as this example shows, are versatile. The preceding code creates a new instance of an ArrayList, along with some related variables to support the demonstration. The code then shows four different types of variables being inserted into the same ArrayList. Next, the code inserts another value into the middle of the list. At no time has the size of the array been declared, nor has a redefinition of the array size been required. The output when run using the ProVB_V2010 project is shown in Figure 8-2.
Visual Basic has additional classes available as part of the System.Collections. Specialized namespace. These classes tend to be oriented around a specific problem. For example, the ListDictionary class is designed to take advantage of the fact that although a hash table is very good at storing and retrieving a large number of items, it can be costly when it contains only a few items. Similarly, the stringCollection and stringDictionary classes are defined so that when working with strings, the time spent interpreting the type of object is reduced and overall performance is improved. Each class defined in this namespace represents a specialized implementation that has been optimized for handling specific data types.
The preceding examples have relied on the use of the For...Next statement, which has not yet been covered. Since you've now covered both arrays and collections, it's appropriate to introduce the primary commands for working with the elements contained in those variable types. Both the For loop and While loop share similar characteristics, and which should be used is often a matter of preference.
The For structure in Visual Basic is the primary way of managing loops. It actually has two different formats. A standard For Next statement enables you to set a loop control variable that can be incremented by the For statement and custom exit criteria from your loop. Alternatively, if you are working with a collection in which the array items are not indexed numerically, then it is possible to use a For Each loop to automatically loop through all of the items in that collection. The following code shows a typical For Next loop that cycles through each of the items in an array:
For i As Integer = 0 To 10 Step 2 arrMylntArrayl(i) = i
The preceding example sets the value of every other array element to its index, starting with the first item, because like all .NET collections, the collection starts at 0. As a result, items 0, 2, 4, 6, 8, and 10 are set, but items 1, 3, 5, 7, and 9 are not explicitly defined because the loop doesn't address those values. In the case of integers, they'll default to a value of 0 because an integer is a value type; however, if this were an array of strings or other reference types, then these array nodes would actually be undefined, i.e., Nothing.
The For Next loop is most commonly set up to traverse an array, collection, or similar construct (for example, a data set). The control variable i in the preceding example must be numeric. The value can be incremented from a starting value to an ending value, which are 0 and 10, respectively, in this example. Finally, it is possible to accept the default increment of 1; or, if desired, you can add a step qualifier to your command and update the control value by a value other than 1. Note that setting the value of step to 0
means that your loop will theoretically loop an infinite number of times. Best practices suggest your control value should be an integer greater than 0 and not a decimal or other floating-point number.
Visual Basic provides two additional commands that can be used within the For loop's block to enhance performance. The first is Exit For; and as you might expect, this statement causes the loop to end and not continue to the end of the processing. The other is Continue, which tells the loop that you are finished executing code with the current control value and that it should increment the value and reenter the loop for its next iteration:
If arrMyIntArrayl.Count <= i Then Exit For If i = 5 Then Continue For arrMylntArrayl (i) = i - 1
Both the Exit For and Continue keywords were used in the preceding example. Note how each uses a format of the If-Then structure that places the command on the same line as the If statement so that no End If statement is required. This loop exits if the control value is larger than the number of rows defined for arrMyIntArrayl.
Next, if the control variable i indicates you are looking at the sixth item in the array (index of five), then this row is to be ignored, but processing should continue within the loop. Keep in mind that even though the loop control variable starts at 1, the first element of the array is still at 0. The Continue statement indicates that the loop should return to the For statement and increment the associated control variable. Thus, the code does not process the next line for item six, where i equals 5.
The preceding examples demonstrate that in most cases, because your loop is going to process a known collection, Visual Basic provides a command that encapsulates the management of the loop control variable. The For Each structure automates the counting process and enables you to quickly assign the current item from the collection so that you can act on it in your code. It is a common way to process all of the rows in a data set or most any other collection, and all of the loop control elements such as Continue and Exit are still available:
For Each item As Object In objMyArrList 'Code Al
While, Do While, and Do Until
In addition to the For loop, Visual Basic includes the While and Do loops, with two different versions of the Do loop. The first is the Do While loop. With a Do While loop, your code starts by checking for a condition; and as long as that condition is true, it executes the code contained in the Do loop. Optionally, instead of starting the loop by checking the While condition, the code can enter the loop and then check the condition at the end of the loop. The Do Until loop is similar to the Do While loop:
Do While blnTrue = True 'Code A1
The Do Until differs from the Do While only in that, by convention, the condition for a Do Until is placed after the code block, thus requiring the code in the Do block to execute once before the condition is checked. It bears repeating, however, that a Do Until block can place the Until condition with the Do statement instead of with the Loop statement, and a Do While block can similarly have its condition at the end of the loop:
'Code A1 Loop Until (blnTrue = True)
In both cases, instead of basing the loop around an array of items or a fixed number of iterations, the loop is instead instructed to continue perpetually until a condition is met. A good use for these loops involves tasks that need to repeat for as long as your application is running. Similar to the For loop, there are Exit Do and
Continue commands that end the loop or move to the next iteration, respectively. Note that parentheses are allowed but are not required for both the While and the Until conditional expression.
The other format for creating a loop is to omit the Do statement and just create a While loop. The While loop works similarly to the Do loop, with the following differences. The While loop's endpoint is an End While statement instead of a loop statement. Second, the condition must be at the start of the loop with the While statement, similar to the Do While. Finally, the While loop has an Exit While statement instead of Exit Do, although the behavior is the same. An example is shown here:
While blnTrue = True If blnFalse Then blnTrue = False End if
If not blnTrue Then Exit While System.Threading.Thread.Sleep(5 00) blnFalse = True End While
The While loop has more in common with the For loop, and in those situations where someone is familiar with another language such as C++ or C#, it is more likely to be used than the older Do-Loop syntax that is more specific to Visual Basic.
Finally, before leaving the discussion of looping, note the potential use of endless loops. Seemingly endless, or infinite, loops play a role in application development, so it's worthwhile to illustrate how you might use one. For example, if you were writing an e-mail program, you might want to check the user's mailbox on the server every 20 seconds. You could create a Do While or Do Until loop that contains the code to open a network connection and check the server for any new mail messages to download. You would continue this process until either the application was closed or you were unable to connect to the server. When the application was asked to close, the loop's Exit statement would execute, thus terminating the loop. Similarly, if the code were unable to connect to the server, it might exit the current loop, alert the user, and probably start a loop that would look for network connectivity on a regular basis.
One warning with endless loops: Always include a call to Thread.Sleep so that the loop only executes a single iteration within a given time frame to avoid consuming too much processor time.
Normally, when a conversion (implicit or explicit) occurs, the original value is read from its current memory location, and then the new value is assigned. For example, to convert a Short to a Long, the system reads the two bytes of Short data and writes them to the appropriate bytes for the Long variable. However, under Visual Basic, if a value type needs to be managed as an object, then the system performs an intermediate step. This intermediate step involves taking the value on the stack and copying it to the heap, a process referred to as boxing. In Chapter 1, in the section titled "Value and Reference Types", a distinction was made regarding how certain types were stored. As noted then, Value types are stored on the stack, while reference values are stored on the heap. As noted earlier, the Object class is implemented as a reference type, so the system needs to convert value types into reference types for them to be objects. This doesn't cause any problems or require any special programming, because boxing isn't something you declare or directly control, but it does affect performance.
If you're copying the data for a single value type, this is not a significant cost, but if you're processing an array that contains thousands of values, the time spent moving between a value type and a temporary reference type can be significant. Thus, if when reviewing code you find a scenario where a value is boxed, it may not be of significant concern. When it becomes something to address is if that boxing is called within a loop that is executed thousands or millions of times. When considering best practices, boxing is something to address when working with large collections and calls that are made repeatedly.
Fortunately, there are ways to limit the amount of boxing that occurs when using collections. One method that works well is to create a class based on the value type you need to work with. This might seem counterintuitive at first because it costs more to create a class. The key is how often you reuse the data contained in the class. By repeatedly using the object to interact with other objects, you avoid creating a temporary boxed object.
Examples in two important areas will help illustrate boxing. The first involves the use of arrays. When an array is created, the portion of the class that tracks the element of the array is created as a reference object, but each element of the array is created directly. Thus, an array of integers consists of an Array object and a set of Integer value types. When you update one of the values with another integer value, no boxing is involved:
Dim arrInt(20) as Integer Dim intMyValue as Integer = 1
Neither of these assignments of an integer value into the integer array that was defined previously requires boxing. In each case, the array object identifies which value on the stack needs to be referenced, and the value is assigned to that value type. The point here is that just because you have referenced an object doesn't mean you are going to box a value. The boxing occurs only when the values being assigned are being transitioned from value types to reference types:
Dim strBldr as New System.Text.StringBuilder() Dim mySortedList as New System.Collections.SortedList() Dim count as Integer For count = 1 to 100
strBldr.Append(count) mySortedList.Add(count, count)
The preceding snippet illustrates two separate calls to object interfaces. One call requires boxing of the value intCount, while the other does not. Nothing in the code indicates which call is which, but the Append method of StringBuilder has been overridden to include a version that accepts an integer, while the Add method of the SortedList collection expects two objects. Although the integer values can be recognized by the system as objects, doing so requires the runtime library to box these values so that they can be added to the sorted list.
When looking for boxing, the concern isn't that you are working with objects as part of an action, but that you are passing a value type to a parameter that expects an object, or you are taking an object and converting it to a value type. However, boxing does not occur when you call a method on a value type. There is no conversion to an object, so if you need to assign an integer to a string using the ToString method, there is no boxing of the integer value as part of the creation of the string. Conversely, you are explicitly creating a new string object, so the cost is similar.
Generics refer to the technology built into the .NET Framework (introduced originally with the .NET Framework version 2.0) that enables you to define a template and then declare variables using that template. The template defines the operations that the new type can perform; and when you declare a variable based on the template, you are creating a new type. The benefit of generics over untyped collections or arrays is that a generic template makes it easier for collection types to be strongly typed. The introduction of covariance in .NET Framework 4 makes it easier to reuse the template code in different scenarios.
The primary motivation for adding generics to .NET was to enable the creation of strongly typed collection types. Because generic collection types are strongly typed, they are significantly faster than the previous inheritance-based collection model. Anywhere you presently use collection classes in your code, you should consider revising that code to use generic collection types instead.
Visual Basic 2010 allows not only the use of preexisting generics, but also the creation of your own generic templates. Because the technology to support generics was created primarily to build collection classes, it naturally follows that you might create a generic collection anytime you would otherwise build a normal collection class. More specifically, anytime you find yourself using the Object data type, you should instead consider using generics.
Was this article helpful?