Building Your First Visual Basic .NET Array

Building Your First Visual Basic .NET Array

The Microsoft .NET Framework class System.Array is the base class of all array types. The System.Array class has properties for determining the rank, length, and lower and upper bounds of an array, as well as methods for creating, accessing, sorting, searching, and copying arrays. An array type is defined by specifying the element type (such as string or integer), the rank (number of dimensions), and the upper and lower bounds of each dimension of the array. An element is a specific position within an array. The length of an array is the number of elements the array can contain.

A Visual Basic .NET array can have one or more dimensions. The dimensionality or rank of an array corresponds to the number of subscripts used to identify an individual element. For example, if you have aMyArray(10,3), you have a two-dimensional array with 0–9, 0–2 elements. You can actually specify up to 60 dimensions, but I personally get confused after three dimensions, and having more than three dimensions is extremely rare.

Every dimension of an array has a lower bound and an upper bound. The lower bound of an array is the starting index of that dimension and is always 0. You can set an initial value for the upper bound when you create the array. (The upper bound is one less than the number of elements in the array.) The elements of an array are contiguous within these bounds. Visual Basic .NET allocates space for an array element corresponding to each index number, so you should avoid declaring an array larger than necessary—the space will be allocated even if there is no data stored there. If you declare an array of 1000 integers but only store 100, the space required to store the additional 900 integers is still allocated in memory and amounts to wasted space.

A multidimensional array can have different bounds for each dimension. These multidimensional arrays can be used for matrix multiplication. Let's say we want a place to store the various labors of Hercules. What better way to store them than in an array? You can dimension an array in several ways. Let's start out with one that might look familiar.

Dim asLaborsOfHercules(13) As String

This statement dimensions an array with 13 elements, 0–12. Each of the elements will have a string data type. Because we told the array how many elements we wanted as well as the data type of the elements, each will be initialized to an empty string. As you recall, an empty string is the default initialization for a string data type.

note

By adding as to the name of the array LaborsOfHercules, I know it is an array of strings. Naming conventions are like a religion—each has its zealots who are certain that theirs is the correct one. You can use any naming convention you want, but be consistent and clear with whichever convention you use.

Now we want to add strings to each of the elements. You can still use the syntax that's familiar from classic Visual Basic, for example:

asLaborsOfHercules(0) = "Kill Nemean Lion"

However, because the array we dimensioned has several methods, you can also use the SetValue method to add string elements. The syntax for this is as follows:

<ArrayName>.SetValue("element contents", element index)

To add data to the first element of our array, use this syntax:

asLaborsOfHercules.SetValue("Kill Nemean Lion", 0)

Let's go ahead and fill our array with the strings we want to store in memory. When you are performing more than two operations on the same object, always remember to use the With...End With construct. Rather than using the brute force method of fully qualifying each and every entry to the array, the With...End With construct holds the variable reference for us. Not only is the code easier to read, but it's much more efficient. If we fully qualified each entry, Visual Basic would have to look up each nested reference for each new element added. And besides, fully qualifying each entry is a lot of typing, as you can see in the code that follows.

asLaborsOfHercules.SetValue("Kill Nemean Lion", 0) asLaborsOfHercules.SetValue("Slay nine-headed hydra of Lerna", 1) asLaborsOfHercules.SetValue("Capture elusive Stag of Arcadia", 2) 

Every time we use a dot, Visual Basic has to resolve it. If we didn't use the With...End With syntax, each time we added a new value, Visual Basic would have to look up the address of our array and resolve any classes it's derived from to ensure it's a valid array for each and every item added.

When you are using a simple array like we are here, the With...End With construct is really used for readability and formatting. However, when you get into nested objects with many dots, the performance benefit is material. The more dots in an expression, the deeper you are going in the object model and the better the performance benefit you'll see using the With…End With construct if you are frequently accessing the object's elements.

Array Boundaries

We first dimension the array, tell it how many elements to add, and finally specify that it will contain string objects.

Dim asLaborsOfHercules(12) As String With asLaborsOfHercules .SetValue("Kill Nemean Lion", 0) .SetValue("Slay nine-headed hydra of Lerna", 1) .SetValue("Capture elusive Stag of Arcadia", 2) .SetValue("Capture wild boar on Mt. Erymantus", 3) .SetValue("Clean Stables of King Augeas of Elis", 4) .SetValue("Shoot monstrous man-eating birds of the" & _ " Stymphalian Marshes", 5) .SetValue("Capture mad bull of Crete", 6) .SetValue("Kill man-eating mares of King Diomedes", 7) .SetValue("Steal Girdle of Hippolyta", 8) .SetValue("Seize cattle of Geryon of Erytheia", 9) .SetValue("Fetch golden apples of Hesperides", 10) .SetValue("Retrieve three-headed dog Cerberus from Hell", 11) .SetValue("Learn Visual Basic .NET", 12) End With

Our array now contains data for elements 0 through 12, giving us 13 elements. If we attempt to add one more element, for example,

asLaborsOfHercules.SetValue("Learn Visual Basic .NET in " & _     "a single day", 13)

we exceed the upper boundary of the array. Whenever this happens, a run-time System.IndexOutOfRangeException error is thrown. Because we can access data beyond the bounds of an array only during run time, the faulty program logic that caused this error can't be detected at design time. Many times it's the user that first discovers this mistake at run time, as you can see in Figure 6-1.

Figure 6-1

This error can't be detected at design time.

To help prevent you from making an error of this sort, the array object contains knowledge of its own structure. You can easily determine the upper and lower bounds of an array, as well as the number of elements it contains, by calling various array methods. Passing in 0 to certain methods gives you information about a one-dimensional array (or the first dimension of a multidimensional array).

For example, GetLowerBound(0) returns the lower bound for the indexes of the first dimension of the array, and GetLowerBound(Rank - 1) returns the lower bound of the last dimension of a multidimensional array. Why - 1? Because if the array contains three dimensions, the first starts at 0, the second at 1, and the third at 2. Here's some code that puts these methods to use. You can see the results in Figure 6-2.

Imports Microsoft.VisualBasic.ControlChars Public Class Form1 Inherits System.Windows.Forms.Form Private Sub Form1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Dim sArrayInfo As String Dim sArrayInfo As String  With asLaborsOfHercules .SetValue("Kill Nemean Lion", 0) .SetValue("Slay nine-headed hydra of Lerna", 1) .SetValue("Capture elusive Stag of Arcadia", 2) .SetValue("Capture wild boar on Mt. Erymantus", 3) .SetValue("Clean Stables of King Augeas of Elis", 4) .SetValue("Shoot monstrous man-eating birds " & _ "of the Stymphalian Marshes", 5) .SetValue("Capture mad bull of Crete", 6) .SetValue("Kill man-eating mares of " * _ "King Diomedes", 7) .SetValue("Steal Girdle of Hippolyta", 8) .SetValue("Seize cattle of Geryon of Erytheia", 9) .SetValue("Fetch golden apples of Hesperides", 10) .SetValue("Retrieve three-headed dog Cerberus " & _ "from Hell", 11) .SetValue("Learn Visual Basic .NET", 12) End With With asLaborsOfHercules sArrayInfo = "The lower bound is: " & _ .GetLowerBound(0) & ControlChars.CrLf sArrayInfo += "The upper bound is: " & _ .GetUpperBound(0).ToString & _ ControlChars.CrLf sArrayInfo += "Number of elements: " & _ .GetLength(0).ToString & _ ControlChars.CrLf sArrayInfo += "Dimension of array: " & _ .Rank.ToString & ControlChars.CrLf End With MessageBox.Show(sArrayInfo, "Array Information") End Sub End Class

Figure 6-2

You can determine array information at run time.

As expected, the lower bound is 0 and the upper bound is 12, giving us 13 elements. So when dimensioning a fixed-size array it's helpful to think that the array is dimensioned to the upper bound. But if you are new to zero-based arrays, you must always remember that the array starts at zero (as do arrays in most other languages).

note

At the end of each line in the message box, we want to add a carriage return and line feed character (CrLf). Remember that these constants are referenced in the Microsoft.VisualBasic.ControlChars namespace. While we could write something like Microsoft.VisualBasic.ControlChars.CrLf to generate the carriage return and line feed characters, there's an easier way. Visual Basic .NET provides a handy means to create an alias for a namespace. Aliases let you assign a friendlier name to just one part of a namespace. By defining an alias right in the Imports statement and assigning the namespace, you can reference the fully qualified namespace by the shorter name.

Imports CtrlChr = Microsoft.VisualBasic.ControlChars

Remember what I said about minimizing the dots? Here we reduced two dots to one. This syntax is more efficient and improves readability of the code. Of course, Imports statements must always be the first lines in a module.

The major problem some novice programmers have with arrays is either overshooting or undershooting array access. Their code tries to access an element beyond the end of the array, which is known as the off by one error. I have read programming books in which the author actually suggests adding an additional element to the end of an array as a "cushion" so that this error won't happen. I find this advice incredibly poor because it not only promotes sloppy programming but, if that end element was actually accessed, invalid data would be there anyway.

If you need to iterate through an array, don't hard-code the boundaries; instead always use the GetLowerBound and GetUpperBound methods to ensure you don't overshoot or undershoot the array bounds. This defensive programming is not only good style, but it also ensures that you don't attempt to access an element outside of those defined. Doing so inadvertently guarantees a run-time error. In the following code, we simply loop through the array and build a string to output.

Dim iIndex As Integer Dim sArrayContents As String = "" For iIndex = asLaborsOfHercules.GetLowerBound(0) To _ asLaborsOfHercules.GetUpperBound(0) sArrayContents += _ asLaborsOfHercules.GetValue(iIndex).ToString & _ CtrlChr.CrLf Next MessageBox.Show(sArrayContents, "Things to do today.")

Now we can be sure we never get that nasty run-time error and the code always runs correctly, as shown in Figure 6-3. You can still use the older LBound(array) and UBound(array) methods from classic Visual Basic, but you should get in the habit of using these newer methods built into the Visual Basic .NET array object.

Figure 6-3

GetLowerBound and GetUpperBound let you work with arrays of all sizes.

Some luminaries in the field of computer science think that you should never access an individual element in an array but should instead always iterate over an entire array sequentially. Their thinking is that random access into an array is conceptually similar to random GoTo statements in a program, and we all know that the GoTo statement is verboten in most professional programming. They state that such random access tends to be undisciplined, error prone, and difficult to prove correct. They suggest that programmers should instead use stacks and queues whose elements can be accessed only sequentially. Luckily, stacks and queues are also built into the .NET Framework; however, I don't subscribe to this belief because at times you do need to randomly retrieve array elements. And because the array object gives us the tools to ensure that we only access legitimate elements, this is not a problem in practice.

Why Arrays Are Based on the System.Array Class

Because arrays in Visual Basic .NET are based on the .NET Framework System.Array class, several operations that aren't traditionally supported by array types are built into Visual Basic .NET arrays. You can now sort and search arrays, a capability that we old C programmers wished was part of Visual Basic for years.

Sorting Arrays

Classic Visual Basic programmers often needed array data to be sorted, but no built-in routines did this. I've written many bubble-and-heap sort routines to sort arrays. I've also seen voodoo programming where programmers added the contents of an array to an invisible list box, set the list box's Sort property to True to sort the data, and then read the data back to the array. While this approach worked, it was slow and used the language in strange and unnatural ways.

We can now use the System.Array type and simply pass in the array we want to sort. We can then call the System.Array Sort method, and the sorting is quickly done for us with no code. Very nice, as you can see in Figure 6-4.

Array.Sort(asLaborsOfHercules)

Figure 6-4

The built-in Sort method makes it easy to sort arrays.

Reversing the Contents of an Array

In addition to sorting, you can now reverse the contents of an array. Note that reverse does not mean a reverse sort, as you might think. Rather, the Reverse method indexes the elements of the array in reverse order. As you can see in Figure 6-5, learning Visual Basic .NET is now the easiest of Hercules' tasks.

Array.Reverse(asLaborsOfHercules)

Figure 6-5

The built-in Reverse method makes it easy to reverse the order of the elements in an array.

Locating Elements

If you need to find a specific string within an array, you simply use the IndexOf method of the System.Array class. By passing in the array to search and the string you're looking for, the array index is returned. If the value is not found, - 1 is returned. IndexOf is overloaded and can search portions of an array. Here's code that locates the index for Hercules' favorite task. Figure 6-6 shows the results.

Dim iIndex As Integer iIndex = Array.IndexOf(asLaborsOfHercules, _ "Learn Visual Basic .NET") MessageBox.Show("In element: " & iIndex.ToString, _ "Where is our string?")

Figure 6-6

The built-in IndexOf method returns the index of a specified value.

Binary Search of an Array

For larger arrays, a very efficient binary search method is available. This method works only on a sorted list or array, however. This routine first takes the contents of the middle element and determines whether the value you are searching for is less than or greater than this middle element. If the value is less, the routine just eliminated 50 percent of the items to be searched because it ignores the upper half of the array. The BinarySearch method then takes the remaining 50 percent of the array and compares your value to the new middle element. If the value is less than that element, the routine then ignores the top 50 percent of the remaining elements, and so on. You must remember that the BinarySearch method works only on sorted arrays. If you forget to sort the array, this method will not find the string. Here's an example of the BinarySearch method. Figure 6-7 shows the results.

Dim iIndex As Integer Array.Sort(asLaborsOfHercules) iIndex = Array.BinarySearch(asLaborsOfHercules, _ "Learn Visual Basic .NET") MessageBox.Show("In element: " & iIndex.ToString, _ "Where is our string?")

Figure 6-7

The built-in BinarySearch method is very efficient.

Arrays in Visual Basic .NET have grown up to provide just about any functionality you might need. With the object-oriented nature of the array object, you simply call a method or property to get what you need. Table 6-1 lists the methods of the System.Array class, and Table 6-2 lists the properties.

Table 6-1  Methods of System.Array

Method

Description

BinarySearch

Overloaded. This searches a one-dimensional sorted array for a value.

Clear

Sets a range of elements in an array to zero or to a null reference (the equivalent of classic Visual Basic's Nothing).

Copy

Overloaded. Copies a section of one array to another array, and performs type downcasting as required.

CreateInstance

Overloaded. Initializes a new instance of the Array class.

IndexOf

Overloaded. Returns the index of the first occurrence of a value in a one-dimensional array or in a portion of an array.

LastIndexOf

Overloaded. Returns the index of the last occurrence of a value in a one-dimensional array or in a portion of an array.

Reverse

Overloaded. Reverses the order of the elements in a one-dimensional array or in a portion of an array.

Sort

Overloaded. Sorts the elements in a one-dimensional array.

Clone

Creates a shallow copy of an Array object.

CopyTo

Copies all the elements of the current one-dimensional array to the specified one-dimensional array, starting at the specified destination array index.

Equals (inherited from Object)

Determines whether the specified object is the same instance as the current object.

GetLength

Gets the number of elements in the specified dimension of the array.

GetLowerBound

Gets the lower bound of the specified dimension in an array.

GetUpperBound

Gets the upper bound of the specified dimension in an array.

GetValue

Overloaded. Gets the values of an array's elements at the specified indexes.

Initialize

Initializes every element of the value-type array by calling the default constructor of the value type.

SetValue

Overloaded. Sets the specified array elements to the specified value.

ToString (inherited from Object)

Returns a string that represents the current object.

Table 6-2  Properties of System.Array

Property

Description

IsReadOnly

Gets a value indicating whether an array is read-only.

IsSynchronized

Gets a value indicating whether access to an array is synchronized (thread-safe).

Length

Gets the total number of elements in all the dimensions of an array.

Rank

Gets the rank (number of dimensions) of an array.

What If I Don't Know How Many Elements I Need Ahead of Time?

In Visual Basic .NET, an array cannot have a fixed size if you want to initialize it with values during the declaration. For example, if you want to declare a specific size for an array, you can use either of the following declarations—they are essentially equivalent:

Dim aNumbers(5) As Integer Dim aNumbers() As Integer = New Integer(5) {} 

Both of these declarations specify an initial size—in this case 5. But because you specified a size during declaration, Visual Basic .NET will not allow you to initialize the array with values like this:

Dim aNumbers(5) as Integer = {0, 1, 2, 3, 4} ' - Error!

Many times you just won't know how many elements will be required in an array. For example, your program might read records from a database and return a varying number of records. Or it might take input from a user that will vary from time to time. You just can't be certain at the time you are coding the program how many elements you will need.

The way to handle this situation is to declare a dynamic array. As the name implies, you can modify the size of a dynamic array during execution with the ReDim statement. By declaring the array without an index, the array is dynamic.

Dim asArray() As String 'Declare a dynamic array ReDim asArray(5) 'Provide 5 elements

You can use the ReDim statement to add new elements to the array as needed. Using the Redim statement erases the contents of the array and adds more elements. If you need to resize the array but keep the original contents, use ReDim Preserve.

ReDim asArray(10) 'Erases the contents and 'resizes the array to 10 ReDim Preserve asArray(15) 'Resizes the array but keeps the 'previous contents

A "gotcha" you have to keep in mind is that if you initialize an array with a specific size, this number is the number of elements, not the upper bound, as was the case in classic Visual Basic. In the preceding example we have 5 elements of integers, but they are indexed from 0 through 4. If you try to exceed the upper limit of an array, you will be presented with the friendly "Index was out of range" error. Again, this error can't be detected during coding but will only show up at execution. So a word to the wise: be extra careful not to address an array element above or below its boundaries.

I would caution you about using the ReDim Preserve statement, however. ReDim allows you to increase or decrease the array size. When the ReDim Preserve keywords are encountered, Visual Basic .NET will create a brand-new array and copy the contents to it. It does this because the array is inherited from System.Array and the .NET runtime defined the original fixed size on creation.

If you do declare a dynamic array, you need to write code to hold the current number of elements. Then, when adding a new element, you have to check to see whether this addition will exceed the current boundaries. If it will, you need to write code using the ReDim Preserve statement in order to preserve the array and increase the number of elements it contains by some constant. I've performed tons of these operations in previous versions of Visual Basic, and they can get complicated. Also, all of the checks that have to be performed can bog down processing. When I cover collections later in this chapter, you'll see some elegant solutions to this frequent problem. When you need to dynamically resize an array, the new ArrayList object makes resizing fast and painless.

Arrays Start at Zero in Visual Basic .NET

As I mentioned, all arrays in the .NET Framework start at zero. The Option Base statement that was used in classic Visual Basic is gone. The zero-based array structure was implemented to simplify processing for the common language runtime. While creating arrays with a non-zero base in Visual Basic. NET is possible, don't try to inherit them because it won't work. If you have not been using a zero base for arrays, my suggestion is that you should start now.

If you really need to create a non-zero-based array, you can do so as follows. This syntax creates an array of 5 integers starting at 10. The array has 5 elements from 10 through 14.

Dim aBadArray As Array = System.Array.CreateInstance(GetType _ (Integer), New Integer(){5}, New Integer() {10})

Yes, I agree that this declaration looks like neo-Babylonian cuneiform, but you can easily test it by displaying the elements in a message box. By always using the GetLowerBound and GetUpperBound methods of the array, you can be sure you never walk off either end of the array. But my strong recommendation is don't do this.

note

You can also use non-zero-based arrays in Visual Basic .NET by calling the NewArray function in the Visual Basic 6 Compatibility Library. However, I'd recommend resisting this temptation also. Anything in the compatibility class is subject to extinction, and using the NewArray function will negatively affect performance. Every reference to the array will require an extra call to the function. Arrays created with the NewArray function might not be compatible with other arrays in your project. And as we now know, they do not conform to common language specification standards. Many minuses for keeping a bad habit; it's not worth it.

Initializing the Array During Declaration

If you want to initialize elements when you declare an array, use the following syntax. With this syntax, the array is sized to fit the number of elements by default.

Dim aiDays() As Integer = {1, 2, 3, 4, 5, 6, 7} 

Other array declarations are written like this.

Dim a(10) As Integer 'An integer array from 0 - 9 Dim s1(2, 2) As String 'Multi-dimensional 0-1, 0-1 Dim s2(,,) As String 'Multi-dimensional sizes not specified ReDim s2(3, 4, 5) 'Defines sizes for s2

Arrays Are Reference Types

Always remember that arrays are reference types. If you dimension an array with a size and data type, the array is automatically initialized to the default initialization type for that particular data type. For example, the default initialization of a string is an empty string. The following code will create an array of five elements, each with an empty string as its content. The message box will display an empty string because each element contains at least something—an empty string.

Dim asArray(5) As String MessageBox.Show(asArray(0)) 'An empty string

However, if you use a dynamic array, the compiler has no idea how many elements will be present because you haven't told it. If you inadvertently try to access an element before the array is initialized to the number of elements, you will get a null object reference error.

Dim asArray() As String MessageBox.Show(asArray(0))

Because the array is a reference type, we know its default initialization is Null, as you can see in Figure 6-8.

Figure 6-8

References are initialized to Null by default.

Remember that when an array is declared, it is inherited from the System.Array class. The Array object has knowledge about itself. It knows its data type, its rank, and its upper and lower bounds. When you declare an array of a value data type, such as an integer, double, or some other type, the array itself also contains the data, as you can see here:

Dim aiIntegerArray() As Integer = {23, 12, 54, 11}

In memory, the array of value types looks like Figure 6-9:

Figure 6-9

An array of value types in memory.

However, if you declare an array of reference types, such as a string, the array will contain references to the string objects and not the strings themselves.

Dim asStringArray() As String = {"eenie", "meenie", _ "minie", "moe"}

Arrays of reference types contain an additional level of indirection. The array elements contain references to the string objects. The actual string objects are stored elsewhere, as is shown in Figure 6-10.

Figure 6-10

Reference types do not store information directly.

While arrays are very useful and fast, like anything else they do have their limitations. An array stores its elements in contiguous memory. Therefore, if you need to access the ith element of your array, the operation is very fast. However, because the elements are stored in contiguous memory, inserting a new element in the middle of the array is difficult. You would have to write twisted code to dimension the array again (ReDim) and then move all the elements after the one you inserted.

Of course, you could simply create a new, larger array and copy the values. But doing this also involves work and costs precious CPU cycles. If the array is sorted, searching for an element can be fast. If an array is unsorted, you might have to access each element to find your value. So, with an array we trade off speed for flexibility. If you need to add and delete items in a nonsequential order, collections are for you. We will examine collections later in the chapter, after we look at a bit more code.



Coding Techniques for Microsoft Visual Basic. NET
Coding Techniques for Microsoft Visual Basic .NET
ISBN: 0735612544
EAN: 2147483647
Year: 2002
Pages: 123
Authors: John Connell

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