A Program Example Using Exception Handling


Let's write a program that generates a list of random numbers and then calculates the mean and standard deviation for the list of numbers . The form we'll use is shown in Figure 19.15.

Figure 19.15. The form for the exception handler sample program.

graphics/19fig15.jpg

The text box associated with the number of values is named txtN and the two text boxes near the bottom of the form are named txtMean and txtSD . The two radio buttons are named rbPopulation and rbSample . The command buttons are named btnCalc and btnExit . I don't really need to get into a statistics lesson here, but the mean is simply the average of a list of numbers and the standard deviation is a measure of the variability of the numbers. The sample standard deviation has one less degree of freedom than does the population standard deviation; hence the adjustment in the Term value in the program code.

The code is presented in Listing 19.3.

Listing 19.3 Code for the Exception Handling Example
 Imports System.Math Imports System.Double Public Class Form1  Inherits System.Windows.Forms.Form ' Windows Form Designer generated code  Private Sub btnCalc_Click(ByVal sender As System.Object, ByVal e As _               System.EventArgs) Handles btnCalc.Click   Dim Values() As Double, Mean As Double, SD As Double   Dim N As Long, i As Integer, Which As Integer   txtmean.Enabled = False   txtSD.Enabled = False   N = CLng(txtN.Text)   ' How many values to generate   ReDim Values(N)     ' Set the array size   Randomize()       ' Seed the random number generator   For i = 0 To N - 1    Values(i) = 101.0 * Rnd()   ' Make up some values   Next   If rbPopulation.Checked = True Then    Which = 1       ' Do population SD   Else    Which = 0       ' Do sample SD   End If   SD = StandardDeviation(Values, Mean, N, Which)   If Not IsInfinity(Mean) And Not IsNaN(SD) Then    txtmean.Enabled = True    txtSD.Enabled = True   End If   txtSD.Text = Format(SD, "###.#####")   txtmean.Text = Format(Mean, "###.#####")  End Sub  Public Function StandardDeviation(ByVal X() As Double, ByRef Mean As _        Double, ByVal N As Double, ByVal Which As Double) As Double   ' Purpose: This function finds the mean and standard deviation   '      of a series of data.   '   ' Argument list:   '  X()    an array of doubles that holds the data   '  Mean   the calculated mean, to be filled in by this function   '  N     the number of observations   '  Which   calculate pop. SD (Which = 1) or sample SD (Which = 0)   '   ' Return value:   '  Double  the standard deviation for the data set. If an error   '       is detected, SD is set to NaN and Mean is set to   '       either positive or negative infinity.   '   ' CAUTION: Note that argument Mean is passed by reference   Dim i As Long, sum As Double, ss As Double, SD As Double   Dim Term As Double   Try    sum = 0.0   ' Yea, I know...they're set to 0 anyway.    ss = 0.0    For i = 0 To N - 1     sum += X(i)     ' Do the running sum     ss += X(i) * X(i)  ' Do sums of squares    Next    Mean = sum / CDbl(N) ' Calculate the mean    If Which = 1 Then   ' Population SD     Term = CDbl(N)    Else         ' Sample SD, lose one degree of freedom     Term = CDbl(N - 1.0)    End If    If IsPositiveInfinity(Mean) Or IsNaN(Mean) Then ' See if bogus     Throw New DivideByZeroException()    End If   Catch e As DivideByZeroException  ' Here if N = 0    sum = PositiveInfinity   Catch e As OverflowException    ' Here if really big value    sum = PositiveInfinity   Catch    sum = NegativeInfinity      ' Here for everything else   Finally    SD = Sqrt(((N * ss) - (sum * sum)) / (N * Term))    If IsInfinity(SD) Or IsNaN(SD) Then     SD = NaN     Mean = PositiveInfinity    End If   End Try   Return SD  End Function  Private Sub btnExit_Click(ByVal sender As System.Object, ByVal e As _               System.EventArgs) Handles btnExit.Click   Me.Dispose()  End Sub  Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As _              System.EventArgs) Handles MyBase.Load   rbSample.Checked = True   txtmean.Enabled = False   txtSD.Enabled = False  End Sub End Class 

At the very top of Listing 19.3, you'll see that we have imported two additional namespaces. The Math namespace is needed because we use the square root method of the Math class. I'll explain the need for the Double namespace in a moment.

The btnCalc Click Event

When the user clicks the Calculate button, a number of tasks are initiated. First, we set the Enabled property of the two output text boxes to False . We do this so that we can better inform the user when an exception has occurred.

Next, we set the number of elements requested by the user to N . We then redimension the number of elements in the Values() array to permit that number of values. The careful reader will notice that we end up creating one more element than the user requested because arrays start with 0 and ReDim sets the highest index value permitted, not the number of elements. Although we could have adjusted the ReDim statement accordingly , leaving it as it is doesn't affect what we are trying to accomplish.

After the array is resized, we fill it in with random values that fall between 0 and 100. Next we determine whether the user wants to calculate the sample or population standard deviation. Now that we have things set up properly, we call the StandardDeviation() function.

The StandardDeviation() Function

The StandardDeviation() function is passed in four arguments: the Values() array that holds the values; a variable (cleverly) named Mean to hold the mean; the number of observations used in the calculations ( N ); and which type standard deviation is to be calculated ( Which ). You can see the call to StandardDeviation() in the btnCalc Click event.

The StandardDeviation() Function Argument List

As you'll recall from our discussion in Chapter 5, Visual Basic .NET makes all arguments passed to functions copies of the original data. This means that each argument is automatically given the ByVal keyword when you type in the statement for the function. This poses a minor problem for us because functions can return only a single value. We want our function to do double-duty and return both the mean and standard deviation. Hmmm

Because I want to return the standard deviation and the mean from the function, I elected to have the function return the value for the standard deviation. After all, the name of the function is StandardDeviation() , so it seems likely that should be the returned value. So, how do we return the mean?

Simple. In the function's argument list, we change the default pass by value for Mean to be pass by reference. This is why ByRef is used with the Mean argument in the StandardDeviation() function definition. This means that we're passing the lvalue of Mean from the call to the function in the btnCalc Click event. Because StandardDeviation() is using the lvalue for Mean from the btnCalc Click event, our function has access to the Mean defined in btnCalc . Therefore, we can change btnCalc 's Mean while we're executing the StandardDeviation() function code.

To prove to yourself that this works, set a breakpoint in the StandardDeviation() code on the line just after the Mean is calculated. The line is

 If Which = 1 Then   ' Population SD 

Now run the program. When you reach the breakpoint, place the cursor over the variable Mean in the line above the breakpoint line and note its value. Now scroll to the variable named Mean in the btnCalc Click event. The values will be the same, even though you've suspended program execution in the code for the StandardDeviation() function. This is proof positive that the function is using the lvalue of Mean in btnCalc to affect its value in the StandardDeviation() function.

Programmer's Tip

graphics/tip_icon.gif

Now you can see why Visual Basic .NET sets procedure arguments to ByVal by default. Using ByRef in a procedure's argument list exposes that variable to change even though the variable appears not to be in scope. That is, a variable defined with local scope in one procedure can be exposed in a second procedure if the variable is passed to the second procedure by reference. This exposure goes counter to any goals of encapsulation, which is why the default is ByVal .


The Exception Handling Code in StandardDeviation()

Once we are into the StandardDeviation() function code, we set the start of the exception handler using the Try keyword. Then we read the data and perform the calculations for the sum ( sum ), sum of squares ( ss ), and the mean ( Mean ). There's an If test that sets the denominator for the standard deviation calculation depending on the value of Which . Then the mean is calculated.

After the mean is calculated, there's the following If statement block:

 If IsPositiveInfinity(Mean) Or IsNaN(Mean) Then     ' See if the number is bogus   Throw New DivideByZeroException() End If 

We do this just in case the user plays mind games and throws in a value of 0 for N in the statement that calculates the mean located immediately above this statement. The If test uses the IsPositiveInfinity( ) and IsNaN() methods to see whether the calculation of Mean threw an exception.

Double Data Type and Numeric Overflow

You're probably saying: "Wait a minute! If N is zero, why doesn't the code throw a divided by zero exception that's handled in the divide by zero Catch block?" Good question.

You've probably seen many examples in other books where that is exactly what happens. However, their code doesn't have our little If test. The reason is because they probably used integer variables for the data. If you perform a divide by zero operation using a Double data type, however, Visual Basic .NET does not throw a divide by zero exception! Instead, it sets the variable to PositiveInfinity , NegativeInfinity , or NaN (Not a Number), depending on the result of the operation performed. These constants are defined in the Double class, which is why we imported its namespace at the top of the program code.

The If test uses the IsPositiveInfinity() and IsNaN() methods to see whether Mean has either of these values after the math operation. If either method returns a logic True, we throw a DivideByZeroException . We throw what?

The Throw Keyword

We can force Visual Basic .NET to generate an exception by using the Throw keyword. In our code, we want to generate a divide by zero exception, so we create a new divide by zero exception with the statement

 Throw New DivideByZeroException() 

This causes Visual Basic .NET to generate a divide by zero exception. When this happens, program control is immediately transferred to the Catch block that we've written for the divide by zero exception. In this case, the Catch block simply sets sum to PositiveInfinity .

The Finally Statement Block

The code found in the Finally statement block is always executed, even if there is no exception. In our case, the code calculates the standard deviation ( SD ). However, if any exception was generated along the way, the variable sum will have been assigned the value PositiveInfinity or NegativeInfinity , depending on the exception. (Notice that we also have a generic Catch for those exceptions we haven't thought of.)

After SD is calculated, we test its value with the IsInfinity() and IsNaN() methods. If either method is logic True, we reset the values for SD and Mean . We then return SD from the call.

Back in the btnCalc Click event, we test the value returned from the StandardDeviation() function to see whether it is a Nan . We also test Mean to see whether it was set to infinity (notice how we use the unary Not operator in the If test). If either condition is true, the two output text boxes remain disabled, which causes their background colors to be set to gray. If the tests are passed, the text boxes are enabled and the values displayed on the normal white background.

Can you think of any use for the Command window while using this program to learn about exception handling?



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