Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning Visual Basic 2005 (2006)

.pdf
Скачиваний:
226
Добавлен:
17.08.2013
Размер:
14.97 Mб
Скачать

Chapter 9

If the code inside the loop does not set intIndex to 10 or above, this loop will just keep going forever. This is a very simple example, but even experienced developers find themselves writing and executing loops whose exit condition can never be satisfied.

Logic errors can be the most difficult to find and troubleshoot, because it is very difficult to be sure that your program is completely free from logic errors.

Another type of logic error occurs when a comparison fails to give the result you expect. Say you made a comparison between a string variable, set by your code from a database field or from the text in a file, and the text entered by the user. You do not want the comparison to be case sensitive, so you might write code like this:

If strFileName = txtInput.Text Then

...perform some logic End If

However, if strFileName is set to Index.HTML and txtInput.Text is set to index.html, the comparison will fail. One way to prevent this logic error is to convert both fields being compared to either uppercase or lowercase. This way, the results of the comparison would be True if the user entered the same text as that contained in the variable, even if the case was different. The next code fragment shows how you can accomplish this:

If strFileName.ToUpper = txtInput.Text.ToUpper Then

...perform some logic End If

The ToUpper method of the String class converts the characters in the string to all uppercase and returns the converted results. Since the Text property of a text box is also a string, you can use the same method to convert the text to all uppercase. This would make the comparison in the previous example equal.

An alternative to using either the ToUpper or ToLower methods of the String class is to use the Compare method of the String class, as shown in the next example. This allows you to compare the two strings ignoring the case of the strings. This was covered in the String Comparison section in Chapter 4.

If String.Compare(strFileName, txtInput.Text, True) Then

. . . perform some logic End If

Since logic errors are the hardest errors to troubleshoot and can cause applications to fail or give unexpected and unwanted results, you must check the logic carefully as you code and try to plan for all possible errors that may be encountered by a user. As you become more experienced you will encounter and learn from the common errors that you and your users make.

One of the best ways to identify and fix logic errors is to use the debugging features of Visual Studio 2005. Using these features, you can find loops that execute too many times or comparisons that do not give the expected result.

276

Debugging and Error Handling

Debugging

Debugging code is a part of life — even experienced developers will make mistakes and need to debug their code. Knowing how to efficiently debug your code can make the difference between enjoying your job as a developer and hating it.

In the next few sections, you’ll create a sample project and then debug that project while learning about the Exception Assistant, working with breakpoints, and learning how to step through your code and to use the Watch and Locals windows to examine variables and objects in your code.

Creating a Sample Project

In this section, you take a look at some of the built-in debugging features in the Visual Studio 2005 development environment through various Try It Out exercises. You will write a simple program and learn how to use the most common and useful debugging features available.

You begin this process by creating a program that will use three classes that you create. Classes and objects are covered in greater detail in the next chapter, but by creating and using these classes, you’ll be able to learn about some of the other features in Visual Basic 2005 as well as learn how to debug your programs. These classes will be used to provide data that will be displayed in a list box on your form. These classes introduce two powerful concepts in particular: the generic class with type constraints and the interface. These concepts will be explained in the following How It Works.

Try It Out

Creating a Sample Project to Debug

1.Create a new Windows Application project called Debugging.

2.In the Solution Explorer window, rename the form to Debug.vb by right-clicking the form and choosing Rename from the context menu. Now click the form in the Forms Designer and then set the form’s properties in the Properties window as shown:

Set Size to 481, 336.

Set StartPosition to CenterScreen.

Set Text to Debug Demo.

3.Next, you want to add some basic controls to the form and set their properties, as shown in the following list:

Create a Button control named btnStart and set these properties: Location = 13, 13; Text = Start.

Create a ListBox control named lstData, and set these properties: Anchor = Top, Bottom, Left, Right; Integral Height = False; Location = 13, 43; Size = 448, 254.

4.Right click the Debugging project in the Solution Explorer, choose Add from the context menu, and then choose the Class sub menu item. In the Add New Item – Debugging dialog box, enter a class name of Customer in the Name field and then click the Add button.

5.Add the following highlighted code to the Customer class:

Public Class Customer

277

Chapter 9

Private intCustomerID As Integer

Private strName As String

Public Sub New(ByVal customerID As Integer, ByVal name As String) intCustomerID = customerID

strName = name End Sub

Public ReadOnly Property CustomerID() As Integer

Get

Return intCustomerID

End Get

End Property

Public Property CustomerName() As String Get

Return strName End Get

Set(ByVal value As String) strName = value

End Set End Property

End Class

6.Before moving on to create the next class, take a quick look at the AutoCorrect option in Visual Studio 2005 so that you can get first-hand experience with this feature. The CustomerName property that you just created should really be a ReadOnly property. Insert the ReadOnly keyword between Public and Property and then click the next line of code.

7.You’ll notice that the Set statement in this property has a blue wavy line underneath it indicating an error. If you hover your mouse over the line of code in error, you’ll get a tool tip informing you that a ReadOnly property cannot have a Set statement.

8.Click the small gray box with a red circle and white exclamation point to display the Error Correction Options dialog box, shown in Figure 9-6.

Figure 9-6

278

Debugging and Error Handling

9.Notice that you have two options to choose from. The option that you want is the second one, which is to remove the Set method. Click the hyperlink to have the AutoCorrect feature remove the Set statement from this property.

10.Now add another class to the Debugging project, called Generics. Then modify the Class statement as highlighted here:

Public Class Generics(Of elementType)

End Class

11.Now add the following highlighted code to the Generics class:

Public Class Generics(Of elementType)

‘This class provides a demonstration of Generics ‘Declare Private variables

Private strKey() As String Private elmValue() As elementType

Public Sub Add(ByVal key As String, ByVal value As elementType) ‘Check to see if the objects have been initialized

If strKey IsNot Nothing Then ‘Objects have been initialized

ReDim Preserve strKey(strKey.GetUpperBound(0) + 1) ReDim Preserve elmValue(elmValue.GetUpperBound(0) + 1)

Else

‘Initialize the objects ReDim strKey(0)

ReDim elmValue(0) End If

‘Set the values strKey(strKey.GetUpperBound(0)) = key elmValue(elmValue.GetUpperBound(0)) = value

End Sub

Public ReadOnly Property Key(ByVal Index As Integer) As String

Get

Return strKey(Index)

End Get

End Property

Public ReadOnly Property Value(ByVal Index As Integer) As elementType Get

Return elmValue(Index) End Get

End Property End Class

12.Add one more class to the Debugging project, called Computer. This is an example of a class that implements the IDisposable interface, which will be explained in the How It Works. Enter the highlighted code below. Once you press the Enter key, Visual Studio 2005 will insert the remaining code listed here automatically.

Public Class Computer

279

Chapter 9

Implements IDisposable

Private disposed As Boolean = False

‘ IDisposable

Private Overloads Sub Dispose(ByVal disposing As Boolean) If Not Me.disposed Then

If disposing Then

‘ TODO: put code to dispose managed resources End If

‘ TODO: put code to free unmanaged resources here End If

Me.disposed = True End Sub

#Region “ IDisposable Support “

‘ This code added by Visual Basic to correctly implement the disposable pattern.

Public Overloads Sub Dispose() Implements IDisposable.Dispose

‘ Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above.

Dispose(True)

GC.SuppressFinalize(Me) End Sub

Protected Overrides Sub Finalize()

‘ Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above.

Dispose(False)

MyBase.Finalize() End Sub

#End Region

End Class

13.Add the following two properties to the end of the Computer class:

Public ReadOnly Property FreeMemory() As String

Get

‘Using the My namespace

Return Format(( _

My.Computer.Info.AvailablePhysicalMemory.ToString \ 1024), _

“#,###,##0”) & “ K”

End Get

End Property

Public ReadOnly Property TotalMemory() As String

Get

‘Using the My namespace

Return Format(( _

My.Computer.Info.TotalPhysicalMemory.ToString \ 1024), _

“#,###,##0”) & “ K”

End Get

End Property

280

Debugging and Error Handling

14.Now switch to the code for the Debug form and add the following highlighted Imports statement:

Imports System.Collections.Generic

Public Class Debug

15.You need to add a few private variable declarations next. Add the following code:

Public Class Debug

‘Using the Generics class

Private objStringValues As New Generics(Of String) Private objIntegerValues As New Generics(Of Integer)

‘Using the List<T> class

Private objCustomerList As New List(Of Customer)

16.Add the following ListCustomer procedure to add customers to the list box on your form:

Private Sub ListCustomer(ByVal customerToList As Customer) lstData.Items.Add(customerToList.CustomerID & _

“ - “ & customerToList.CustomerName)

End Sub

17.The rest of the code that will be added will be added to the Start button Click event handler. Select btnStart in the Class Name combo box at the top of the Code Editor and then select the Click event in the Method Name combo box. Add this highlighted code to the Click event handler:

Private Sub btnStart_Click(ByVal sender As Object, _

ByVal e As System.EventArgs) Handles btnStart.Click

‘Declare variables Dim strData As String

lstData.Items.Add(“String variable data:”) If strData.Length > 0 Then

lstData.Items.Add(strData) End If

‘Add an empty string to the ListBox lstData.Items.Add(String.Empty)

‘Demonstrates the use of the List<T> class lstData.Items.Add(“Customers in the Customer Class:”) objCustomerList.Add(New Customer(1001, “Henry For”)) objCustomerList.Add(New Customer(1002, “Orville Wright”)) For Each objCustomer As Customer In objCustomerList

ListCustomer(objCustomer)

Next

‘Add an empty string to the ListBox lstData.Items.Add(String.Empty)

281

Chapter 9

‘Demonstrates the use of Generics

lstData.Items.Add(“Generics Class Key/Value Pairs using String Values:”) objStringValues.Add(“1001”, “Henry Ford”) lstData.Items.Add(objStringValues.Key(0) & “ = “ & _

objStringValues.Value(0))

‘Add an empty string to the ListBox lstData.Items.Add(String.Empty)

‘Demonstrates the use of Generics

lstData.Items.Add(“Generics Class Key/Value Pairs using Integer Values:”) objIntegerValues.Add(“Henry Ford”, 1001) lstData.Items.Add(objIntegerValues.Key(0) & “ = “ & _

objIntegerValues.Value(0))

‘Add an empty string to the ListBox lstData.Items.Add(String.Empty)

‘Demonstrates the use of the Using statement

‘Allows acquisition, usage and disposal of the resource lstData.Items.Add(“Computer Class Properties:”)

Using objMemory As New Computer lstData.Items.Add(“FreeMemory = “ & objMemory.FreeMemory)

lstData.Items.Add(“TotalMemory = “ & objMemory.TotalMemory) End Using

‘Add an empty string to the ListBox lstData.Items.Add(String.Empty)

‘Demonstrates the use of the Continue statement Dim strPassword As String = “POpPassword”

Dim strLowerCaseLetters As String = String.Empty ‘Extract lowercase characters from string

For intIndex As Integer = 0 To strPassword.Length - 1 ‘Demonstrates the use of the Continue statement

‘If no uppercase character is found, continue the loop

If Not strPassword.Substring(intIndex, 1) Like “[a-z]” Then ‘No upper case character found, continue loop

Continue For End If

‘Lowercase character found, save it

strLowerCaseLetters &= strPassword.Substring(intIndex, 1)

Next

‘Display lowercase characters lstData.Items.Add(“Password lower case characters:”) lstData.Items.Add(strLowerCaseLetters)

End Sub

18.Before examining how the code works, hover your mouse over the Error List tab at the bottom of the IDE so that the Error List window appears as shown in Figure 9-7. If the Error List tab is not visible, select View Other Windows Error List from the menu bar. Notice that you have one warning about a potential error in your code. The line in question will cause an error when

282

Debugging and Error Handling

you run your project; however, this is deliberate and is intended to demonstrate some of the debugging capabilities of Visual Studio 2005. You can ignore this warning for now, because you’ll be correcting it shortly.

Figure 9-7

How It Works

After building the UI for the Debugging project, which is very straightforward, you add the Customer class. This class is also straightforward and contains two private variables, a constructor, and two properties.

The two variables in the Customer class are declared as Private, which means that these variables will be accessible only to the procedures in the class:

Public Class Customer

Private intCustomerID As Integer

Private strName As String

The constructor for this class — a method called whenever a new object of this class is to be created — is defined as a Public procedure with a procedure name of New. All constructors for classes in the .NET Framework must be declared with a procedure name of New.

This constructor accepts two input parameters: customerID and name. The parameters are used to set the values in the private variables defined for this class:

Public Sub New(ByVal customerID As Integer, ByVal name As String) intCustomerID = customerID

strName = name End Sub

Two properties are defined: CustomerID and CustomerName. These are read-only properties, meaning that the consumer of this class can use these properties only to read the Customer ID and customer name; consumers cannot change them:

Public ReadOnly Property CustomerID() As Integer

Get

Return intCustomerID

End Get

End Property

Public Property CustomerName() As String

Get

283

Chapter 9

Return strName

End Get

End Property

End Class

The next class that you add to the Debugging project is the Generics class. This class will be used to demonstrate the use of Generics in Visual Basic 2005.

The Collections class in the .NET Framework allows you to store data in the collection in a key/value pair. The key is always a string value that identifies the value, also known as an item. The item is defined as an object, which allows you to use the Collection class to store any data type that you want in the item. So, for instance, you can use the Collection class to store Integer values or you can use it to store String values. No type checking is performed. This lack of specificity can lead to performance problems as well as run-time problems.

Suppose you intend to use the Collection class to store Integer values. If (through poor coding practices) you allowed a String value to be added to the collection, you would not receive a run-time error when adding the item, but you could receive one when you tried to access the item.

The performance problems that you will encounter are the conversion of the data going into the collection and the data coming out of the collection. When you add an item to the collection, the data must be converted from its native data type to an Object data type, since that is how the Item property is defined. Likewise, when you retrieve an item from the collection, the item must be converted from an Object data type to the data type that you are using.

In Chapter 5, when working with ArrayLists (which are a kind of collection), you solved the problem of being able to store items of the wrong type by creating a strongly typed collection class. This did not solve the performance problem. Both problems are solved in Visual Basic 2005 through Generics and through the introduction of type constraints. A type constraint is specified on a class such as Collection by using the Of keyword followed by a list of type name placeholders that are replaced by actual type names when an object of the class is created. This provides type safety by not allowing you to add an item that is not of the same data type that was defined for the class. It also improves performance because the item does not have to be converted to and from the Object data type. The data type for the item will be defined using the data type that was defined for the class. You’ll see how all of this works in more detail as you explore the rest of the code and as you go through the debugging process.

After adding the Generics class, you modify the class by adding a type constraint using the Of keyword and defining a type list, which in this case contains only one type. This type name is a placeholder that will be used throughout the class to represent the data type that this class will be working with. The actual data type is defined when an object of the class is created, as you’ll see later in your code:

Public Class Generics(Of elementType)

End Class

You add two private variables to this class, with both of these variables being defined as an array. The first variable is a defined as a String data type, while the second variable is defined as a generic data type which will be set when an object of the class is created. Notice that you have used the type name elementType, which was defined at the class level. This type name will be replaced automatically by the data type that is used to create the Generics object.

284

Debugging and Error Handling

Public Class Generics(Of elementType)

‘This class provides a demonstration of Generics

‘Declare Private variables Private strKey() As String Private elmValue() As elementType

The Add method allows you to add items to your collection. Notice that this method accepts two parameters; one for the key and the other for the value, making a key/value pair. The key parameter is always a string value, and the value parameter will be defined using the data type that is used when a Generics collection is created.

The first thing that you want to do in this procedure is to see whether the variable arrays have been initialized. You do this by using the IsNot operator and comparing the strKey array to a value of Nothing. If the array is not equal to a value of Nothing, the array has already been initialized, and you simply need to increment the array dimension by 1. This is done by first getting the current upper bounds of the array and then adding 1 to it.

If the variable arrays have not been initialized, you need to initialize them using the ReDim statement as shown in the Else statement in the code that follows.

Once the arrays have been expanded or initialized, you then add the key and value to the arrays:

Public Sub Add(ByVal key As String, ByVal value As elementType) ‘Check to see if the objects have been initialized

If strKey IsNot Nothing Then ‘Objects have been initialized

ReDim Preserve strKey(strKey.GetUpperBound(0) + 1) ReDim Preserve elmValue(elmValue.GetUpperBound(0) + 1)

Else

‘Initialize the objects ReDim strKey(0)

ReDim elmValue(0) End If

‘Set the values strKey(strKey.GetUpperBound(0)) = key elmValue(elmValue.GetUpperBound(0)) = value

End Sub

You add two read-only properties to this class to return the key and the value for a key/value pair. Notice that the Value property is defined to return the data type that will be used when a Generics object is created.

Public ReadOnly Property Key(ByVal Index As Integer) As String

Get

Return strKey(Index)

End Get

End Property

Public ReadOnly Property Value(ByVal Index As Integer) As elementType Get

285