|I l @ ve RuBoard|
Exception Handling Redux
Mistakes happen. Errors are fact of life. That error handling in applications is important is beyond obvious, but Visual Basic developers have had their own set of challenges. The error handling mechanisms of the past are not really suited to larger-scale applications. Visual Basic .NET supports the classic On Error syntax, but it also introduces structured exception handling.
On this subject, we are very
Exception Handling Basics
Exception handling is based on a throw-catch metaphor. When an exception is generated by a block of code, we say that it "
PublicSubTest() Try 'Dosomeworkthatmightcauseanexception CatchexAsException MsgBox(ex.ToString()) EndTry EndSub
Of course, you can get more sophisticated in how you handle exceptions. It is important to note that exceptions are just another type of class that all inherits from the base class System.Exception . Therefore, you can use the Catch statement as an exception filter and specify only the exceptions that you are able to handle:
PublicSubTest() Try 'Dosomeworkthatmightcauseanexception CatchexAsFileNotFoundException MsgBox("Couldnotfindthefile: " +ex.FileName) CatchexAsSecurityException MsgBox("Youdonothavepermissionstoaccessthisobject") EndTry EndSub
Here you can see how it is possible to catch two different types of exceptions and provide custom error handling. Note that if any other exception type is produced in our Try block, the exception will not be caught in this code. This is on purpose, and it is how you should approach exception handling in your own code. Generally speaking, you should never catch an exception unless you intend to do something with it. End of story.
The last part of this particular discussion of the
block has to do with the
code block contains code that is
DimconnAsNewSqlConnection("Aconnectionstring") DimcmdAsNewSqlCommand("Select*fromAuthors",conn) Try conn.Open() cmd.ExecuteReader() CatchexAsSqlException MsgBox(ex.ToString()) Finally 'Thisalwaysgetscalled,whetheranexceptionhappensornot If(NotconnIsNothing)AndAlso_ (conn.State=ConnectionState.Open)Then conn.Close() EndIf EndTry
Alternatively, you can forgo the catch statement altogether and just include the Finally block:
DimconnAsNewSqlConnection("Aconnectionstring") DimcmdAsNewSqlCommand("Select*fromAuthors",conn) Try conn.Open() cmd.ExecuteReader() Finally 'Thisalwaysgetscalled,whetheranexceptionhappensornot If(NotconnIsNothing)AndAlso_ (conn.State=ConnectionState.Open)Then conn.Close() EndIf EndTry
This is a way of saying that you can't handle the exceptions that might be generated by the code in the Try block, but you should always close the connection, regardless. Pretty cool, huh?
Throwing exceptions yourself is not exactly difficult. The .NET Framework makes it that much easier by defining a whole set of exception classes that you can use:
PublicSubErrorSub() 'Dostuff ThrownewException("ErrorSubfailedbecauseyoumadeamistake!") EndSub
Don't use the System.Exception class if a more appropriate exception handling type is available to you. If your code is failing because a necessary file does not exist, use an instance of the FileNotFoundException class ”it has additional properties that can be useful for debugging purposes.
An interesting property of .NET exceptions is that they can contain other exceptions. The
class defines a property called
. This property allows you to access an exception that is contained by another. It also
PublicSubErrorSub() 'Dostuff Try 'Dostuffthatmightfail. CatchexAsSqlException ThrownewException("ErrorSubfailedbecauseyoumadeamistake!",ex) EndTry EndSub
That's pretty much all there is to throwing. The newly thrown exception will have an InnerException that contains the SqlException caught by our Catch statement.
Now that we've covered the basics of throwing and catching exceptions, it seems appropriate to take a step back and look at exception handling practices you should avoid.
Exception Handling No-Nos
Structured exception handling is no panacea. There are a couple of rules you should follow to maximize the benefits and efficiency of exception handling:
It's not unheard of for developers to get exception-handler-happy. When that happens, we see code like this:
PublicSubTest() Try 'Dosomeworkthatmightcauseanexception CatchexAsException Throwex'Don'tdothis!! EndTry Try 'Dosomemoreworkthatmightcauseanexception CatchexAsException IfTypeOfexIsFileNotFoundExceptionThen 'Dosomethingtorecover Else Throwex'Don'tdothis!! EndIf EndTry EndSub
In the first Try block, the exception is caught and immediately rethrown ”a bad idea and a waste of resources. If you can't handle the exception, don't catch it. It's that simple. The second Try block does something similarly foolish. It catches all possible exceptions and checks the type to see if it is a FileNotFoundException to handle the error. If the caught exception is not a FileNotFoundException , the method just rethrows the caught exception. This also is very inefficient and unnecessary. You can get the same result by being more specific in your Catch statement. Always remember the first rule of exception handling: if you don't do anything with it, don't catch it. Fixing the previous example results in code like the following:
PublicSubTest() 'Dosomeworkthatmightcauseanexception Try 'Dosomemoreworkthatmightcauseanexception CatchexAsFileNotFoundException 'Dosomethingtorecover EndTry EndSub
Remember, exceptions are
The .NET Framework defines a wide variety of exceptions types. But sometimes the right kind of exception just isn't available, or you face a whole class of problems that have nothing to do with file or SQL exceptions and are
All exceptions in Visual Basic .NET inherit from the System.Exception class. The .NET Framework further defines exceptions into two main categories: the System.ApplicationException class and the System.SystemException . The System.ApplicationException class is intended to be the base exception class for all application-specific exceptions you define. Most exceptions defined by .NET Framework derive from System.SystemException .
As much as possible, you should use the existing exceptions defined by the .NET Framework. You should define new exceptions only if the existing exceptions don't suffice. When you define a new exception, create your own base exception that's derived from the System.ApplicationException class. You can then build your application's exception hierarchy off of this base class. The following example demonstrates creating a base exception class called MyAppException that's derived from System.ApplicationException . It also shows how to create a specific exception type ( UserConfigException ) from MyAppException that provides additional information about the error that occurred.
PublicClassMyAppException InheritsSystem.ApplicationException PublicSubNew() MyBase.New() EndSub PublicSubNew(ByValmessageAsString) MyBase.New(message) EndSub PublicSubNew(ByValmessageAsString,_ ByValinnerExceptionAsException) MyBase.New(message,innerException) EndSub EndClass PublicClassUserConfigException InheritsMyAppException Privatem_fileNameAsString PublicSubNew(ByValmessageAsString,ByValfileNameAsString) MyBase.New(message) m_fileName=fileName EndSub PublicReadOnlyPropertyFileName()AsString Get Returnm_fileName EndGet EndProperty EndClass
You'll see many examples of exception handling throughout this book. If you need more information, see the MSDN documentation that comes with Visual Basic .NET.
|I l @ ve RuBoard|