In addition to simply watching for them and screaming "Error!" there are a few other things you should know about error management in Visual Basic programs.
Believe it or not, there are times when you might want to generate run-time errors in your code. In fact, many of the run-time errors you encounter in your code occur because Microsoft wrote code in the Framework Class Libraries that specifically generates errors. This is by design.
Let's say that you had a class property that was to only accept percentage values from 0 to 100, but as an Integer data type.
Private StoredPercent As Integer Public Property InEffectPercent() As Integer Get Return StoredPercent End Get Set(ByVal value As Integer) StoredPercent = value End Set End Property
There's nothing grammatically wrong with this code, but it will not stop anyone from setting the stored percent value to either 847 or 847, both outside the desired range. You can add an If statement to the Set accessor to reject invalid data, but properties don't provide a way to return a failed status code. The only way to inform the calling code of a problem is to generate an exception.
Set(ByVal value As Integer) If (value < 0) Or (value > 100) Then Throw New ArgumentOutOfRangeException("value", _ value, "The allowed range is from 0 to 100.") Else StoredPercent = value End If End Set
Now, attempts to set the InEffectPercent property to a value outside the 0-to-100 range will generate an error, an error that can be caught by On Error or Try...Catch error handlers. The Throw statement accepts a System.Exception (or derived) object as its argument, and sends that exception object up the call stack on a quest for an error handler.
Similar to the Throw statement is the Err.Raise method. It lets you generate errors using a number-based error system more familiar to Visual Basic 6.0 and earlier environments. Personally, I recommend that you use the Throw statement, even if you employ unstructured error handling elsewhere in your code.
Mixing Error-Handling Methods
You are free to mix both unstructured and structured error-handling methods broadly in your application, but a single procedure or method may only use one of these methods. That is, you may not use both On Error and Try...Catch...Finally in the same routine. A routine that uses On Error may call another routine that uses Try...Catch...Finally with no problems.
Now you may be thinking to yourself, "Self, I can easily see times when I would want to use unstructured error handling, and other times when I would opt for the more structured approach." It all sounds very reasonable, but let me warn you in advance that there are error-handling zealots out there who will ridicule you for decades if you ever use an On Error statement in your code. For these programmers, "object-oriented purity" is essential, and any code that uses non-object methods to achieve what could be done through an OOP approach must be destroyed.
I'm about to use a word that I forbid my nine-year-old son from using. If you have tender ears, cover them now, though it won't protect you from seeing the word on the printed page.
Rejecting the On Error statement like this is just plain stupid. As you may remember from earlier chapters, everything in your .NET application is object-oriented, because all the code appears in the context of an object. If you are using unstructured error handling, you can still get to the relevant exception object through the Err.GetException() method, so it's not really an issue of objects. Determining when to use structured or unstructured error handling is no different from deciding to use C# or Visual Basic to write your applications. For most applications, the choice is irrelevant. There may be some esoteric features that one language has that may steer you in that direction (such as optional method arguments in Visual Basic), but the other 99.9% of the features are pretty much identical.
The same is true of error-handling methods. There may be times when one is just plain better than the other. For instance, consider the following code that calls three methods, none of which include their own error handler.
On Error Resume Next RefreshPart1() RefreshPart2() RefreshPart3()
Clearly I don't care if an error occurs in one of the routines or not. If an error causes an early exit from RefreshPart1, the next routine, RefreshPart2, will still be called, and so on. I often need more diligent error-checking code than this, but in low-impact code, this is sufficient. To accomplish this same thing using structured error handling would be a little more involved.
Try RefreshPart1() Catch End Try Try RefreshPart2() Catch End Try Try RefreshPart3() Catch End Try
That's a lot of extra code for the same functionality. If you're an On Error statement hater, then by all means, use the second block of code. But if you are a more reasonable programmer, the type of programmer who would read a book like this, then use each method as it fits into your coding design.
The System.Exception Class
The System.Exception class is the base class for all structured exceptions. When an error occurs, you can examine its members to determine the exact nature of the error. You also use this class (or one of its derived classes) to build your own custom exception in anticipation of using the Throw statement. Table 9-1 lists the members of this object.
Classes derived from System.Exception may include additional properties that provide additional detail for a specific error type.
The Err Object
The Err object provides access to the most recent error through its various members. Anytime an error occurs, Visual Basic documents the details of the error in this object's members. It's often accessed within an unstructured error handler to reference or display the details of the error. Table 9-2 lists the members of this object.
The Debug Object
Visual Basic 6.0 (and earlier) included a handy tool that would quickly output debug information from your program, displaying such output in the "Immediate Window" of the Visual Basic development environment.
Debug.Print "Reached point G in code"
The .NET version of Visual Basic enhances the Debug object with more features, and a slight change in syntax. The Print method is replaced with WriteLine; a separate Write method outputs text without a final carriage return.
Debug.WriteLine("Reached point G in code")
Everything you output using the WriteLine (or similar) method goes to a series of "listeners" attached to the Debug object. You can add your own listeners, including output to a work file. But the Debug object is really only used when debugging your program. Once you compile a final release, none of the Debug-related features work anymore, by design.
If you wish to log status data from a released application, consider using the My.Application.Log object instead (or My.Log in ASP.NET programs). Similar to the Debug object, the Log object sends its output to any number of registered listeners. By default, all output goes to the standard debug output (just like the Debug object) and to a log file created specifically for your application's assembly. See the online help for the My.Application.Log Object for information on configuring this object to meet your needs.
Other Visual Basic Error Features
The Visual Basic language includes a few other error-specific statements and features that you may find useful: