For Loops and Arrays


For Loops and Arrays

A very common use for any loop structure is to process data arrays. Create a new project and call it PhoneNumbers. Add control objects to the form as shown in Figure 12.4.

Figure 12.4. Component objects for PhoneNumbers project.

graphics/12fig04.jpg

There are two labels, three text boxes, and three buttons . These objects should all be pretty familiar to you now. Note that txtResult has the Multiline property set to True and the Scrollbars property is set to Vertical . Everything else is old hat by now. Some of the code, however, is not.

Structure Data Types

As you can probably tell from the form in Figure 12.4, our program is going to collect a set of names and phone numbers and display them in a text box. Now, we could stuff the names into one array, and then stuff the phone numbers into another array and sort of co-process the two arrays in parallel. It would work, and we'd probably do it that way if we didn't have better alternatives. After all, if the only tool you have is a hammer , pretty soon all your problems look like a nail. Not so with Visual Basic .NET. We have all kinds of tools available to us. Let's look at one that is especially suited to the task at hand. It's called the Structure .

Programmer's Tip

graphics/tip_icon.gif

Earlier versions of Visual Basic supported something called user-defined data types. Indeed, you may hear programmers refer to user -defined data types as UDTs. UDTs have been replaced in Visual Basic .NET with Structure s. Although this is a form of abandonment for UDTs, it's a step forward for us because most other OOP languages (C++, C#, and Java) support Structure s.


The general form for a Structure declaration is

 [  AccessSpecifier  ]  Structure   StructureTag   StructureMemberList  End Structure 

The keyword Structure begins the structure declaration. It can be preceded by an access specifier such as Public , Private , or Friend . For the moment, however, we're ignoring the optional access specifier until we're ready to discuss it in Chapter 15, "Encapsulation."

The Structure keyword is followed by the StructureTag , or name , you want to give to the structure. Next comes the declarations for the members that you want to have in the structure. The End Structure keywords mark the end of the list of Structure members. The statements between the StructureTag and the End Structure keywords are the Structure statement block.

In our program, we declare a Structure named PhoneList as shown in the following code fragment:

 Structure PhoneList   Dim Name As String  Dim Phone As String End Structure 

Here we have declared a Structure with the structure tag name of PhoneList . The Structure has two members, Name and Phone , both of which are String data types. Notice how the structure members use the Dim statement you've seen so many times before. The end of the structure member list and the Structure declaration itself are marked by the End Structure keywords.

Cool! Now we have a data structure named PhoneList that we can shove names and phone numbers into. WRONG!

Structures Are Cookie Cutters, Not Variables

Notice how careful we were to use the word declaration in the preceding paragraphs. Remember our discussion from Chapter 4 when we talked about defining variables? I stressed that the key element in defining a variable is that the operating system must hand back a valid lvalue for the data item to Visual Basic .NET and record it in the symbol table. Use your highlighter pen and highlight the last sentence . It's very important.

In the Structure code fragment shown earlier, the End Structure keywords tell Visual Basic .NET, "Okay, you now know all the members that belong to the PhoneList Structure . Make a cookie cutter that looks like this and keep it in the symbol table, but don't whack out any cookies with it yet."

The purpose of the Structure - End Structure keywords is to declare a list of member variables that can exist in the program. No storage has been allocated for the PhoneList Structure . Therefore, there are no PhoneList variables yet. Obviously, this also means there are no lvalues yet, either.

To get a PhoneList variable we can use in our program, we need to define one first. We can define a PhoneList variable as follows :

 Dim MyList() As PhoneList 

This statement defines a dynamic array of type PhoneList named Mylist() . Obviously, we'll set the array size later in the program. The key, however, is that we now have a variable named MyList() that exists in the symbol table and has an lvalue . (Actually, the lvalue of a pointer variable is defined at this point. However, the essential point is that an lvalue does now exist.)

Structures may not have local scope. Therefore, we need to place our structure declaration outside of any procedure definition. You'll usually give them module or namespace scope.

The concept of a Structure enables us to group related data together and treat it as a single data item. Plain old arrays require that the data in the array be homogeneous. That is, all of the data in the array must be of the same data type. No so for structures. The Structure members can be whatever data type we want them to be. Yet, the really neat thing is that we can reference this group of related (albeit dissimilar) data by a single variable name. Kim Brand once called structures "arrays for adults" because the array can have complex data members. It's a fitting description.

The btnAdd Click Event

It's pretty obvious that we want the user to type in a name and phone number and then click the Add button to add it to our list. Listing 12.4 shows the code for the btnAdd Click event.

Listing 12.4 Code for the btnAdd Click Event
 Private Sub btnAdd_Click(ByVal sender As System.Object, ByVal e As _              System.EventArgs) Handles btnAdd.Click  Static Count As Integer = 0  If txtname.Text <> "" And txtPhone.Text <> "" Then   Count += 1   ReDim Preserve MyList(Count)   MyList(Count - 1).Name = txtName.Text   MyList(Count - 1).Phone = txtPhone.Text   txtName.Text = ""   ' Clear for next entry   txtPhone.Text = ""   txtName.Focus()  Else   Beep()   MessageBox.Show("Must enter both a name and a number")  End If End Sub 

First, we define a variable named Count with the Static storage class. Why use the Static storage class? The reason is because Static variables can retain their values after program control leaves the procedure in which they're defined. In other words, Static variables have a lifetime that equals that of the program itself. Non- Static local variables do not. Because we want to maintain a count of the number of names and phone numbers that are entered, Count must be defined as a Static .

Programmer's Tip

graphics/tip_icon.gif

True, we could define Count with module scope and it would properly track the count of the number of names and numbers entered. However, giving Count module scope crushes our goal of hiding the data as much as possible. By making it a Static inside of btnAdd , only the code in btnAdd has access to Count . Hiding the data in this way is almost always a good thing.


Although the initialization of Count to 0 is not necessary (Visual Basic .NET does this automatically), I still prefer to initialize it to 0 as a form of documentation of intent. That is, it tells everyone that we intend to initialize Count to 0 even if Visual Basic .NET ever decides not to. (Things could change, right?)

Next, the code uses an If statement to make sure that the user entered both a name and a phone number. A logical And operator is used for this check. Notice that if either text box is empty, an error message is displayed.

If all goes well, we enter the Then statement block. First, we increment Count by 1. We then dynamically allocate enough memory for Count elements of the MyList() array. We use the Preserve keyword because the second (and subsequent ) time we enter this procedure, we want to preserve all the previously entered names and phone numbers in the array. This is exactly what the Preserve keyword does for us. (If this is fuzzy to you, review Chapter 7, "Arrays.")

The next two statements takes the string data stored in the two text boxes and assigns them into the appropriate members of the PhoneList structure. Why is the array subscripted MyList(Count - 1) ?

 MyList(Count - 1).Name = txtName.Text 

The reason is because arrays start with element 0, but Count has already been increment to 1. Therefore, even though we might be entering the first person in the list (that is, Count = 1 ), that first person is actually stored in element 0 in the MyList() array. Think about it.

Once the data is stored in the array, we clear out the text boxes, and move the cursor back to the txtName text box to get ready for another name. The Focus() method of a text box is used to set the focus to the associated text box. Therefore, the Focus() method moves the cursor into the text box.

Programmer's Tip

graphics/tip_icon.gif

The astute reader will recall from our discussion in Chapter 7 that ReDim statements are relatively slow. This is because ReDim statements cause messages to be sent to the Windows memory manager. Therefore, isn't it inefficient to call ReDim each time we add a new name and number? Picky, picky, picky. Yes, it is inefficient. However, because Visual Basic .NET can do several thousand ReDim s in the time it takes you to hit the next keystroke, we can live with it in this case. Keyboard input and output (I/O) and mouse clicks are so slow relative to a ReDim , it will have no performance impact on our program in this situation.


The btnDone Click Event

When the user has finished entering the names and phone numbers, she clicks the Done button. This causes the list of names and phone numbers to be displayed in the txtList text box. The code for the btnDone Click event is shown in Listing 12.5.

Listing 12.5 Code for the btnDone Click Event
 Private Sub btnDone_Click(ByVal sender As System.Object, ByVal e As _              System.EventArgs) Handles btnDone.Click  Dim i As Integer  Dim NewLine As String  NewLine = Chr(13) & Chr(10)  txtList.Text = ""  For i = 0 To UBound(MyList)   txtList.Text += MyList(i).Name & "  " & MyList(i).Phone & NewLine  Next End Sub 

The code defines several working variables and the NewLine string. The txtList text box is then cleared and we start executing the For loop. Because UBound() returns the highest valid index of an array, we can use it to set the terminating value of i in the For loop.

The loop counter i is initialized to because that's the starting element of the MyList() array. The statement block is a single statement that displays the name and phone number for each person entered by the user. The NewLine string simply causes each entry to start on a new line in the text box.

Notice how we use the loop counter variable i to index into the MyList() array. For loops are perfect for sequential processing of arrays. By the way, could we also use the code shown in the following code fragment?

 For i = UBound(MyList) To 0 Step -1   txtList.Text += MyList(i).Name & "  " & MyList(i).Phone & NewLine Next 

Yes, it will work, but it also shows a small bug in our program. Because we ReDim for one more index than we actually use, the program never uses the last element in the MyList() array. When you display the list in normal order, this empty array is actually displayed, but we don't notice it because all the MyList() members are unused (empty). However, because the For loop in the preceding code fragment displays the list in reverse order, we can see that the first element displayed (which is the last one in the array) is actually empty. This must also mean that although UBound() is doing its thing correctly, the count of the number of names and phone numbers is actually one less than UBound() tells us. (How would you fix this? Remember: Never use an H-bomb to kill an ant.)



Visual Basic .NET. Primer Plus
Visual Basic .NET Primer Plus
ISBN: 0672324857
EAN: 2147483647
Year: 2003
Pages: 238
Authors: Jack Purdum

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