7. Exception Handling

 

Henceforth, all the topics would be covered in considerable depth. In one of the previous chapters, we enlightened you about the errors that occurred only at run time, which the compiler could not detect. These errors are termed as 'exceptions'. The occurrence of an exception signifies that something extra-ordinary has transpired, which need not necessarily be an error.

 

For instance, in a subroutine, some of the potential errors that may occur are:

     The parameter supplied to it could be incorrect.

     There could be difficulty in opening a file on disk, etc.

 

Since a subroutine has no means of returning values to indicate success or failure, it becomes difficult to identify the cause or the type of error that has occurred.

 

To be bailed out of a predicament like this, the concept of exceptions comes in handy. For every distinct type of error that occurs, a unique exception can be thrown.

 

a.vb

public class zzz

shared sub Main()

dim a as System.Exception

a = new System.Exception("hi")

Throw a

end sub

end class

 

>vbc a.vb

>a

 

Error

Unhandled Exception: System.Exception: Hi

   at zzz.Main()

 

The Throw statement is the only pathway to execute an exception. This keyword requires an Exception object. Therefore, the object 'a' is declared as an instance of the Exception class, where the parameter of "hi" is passed to the constructor. The Throw statement now 'throws' or 'raises' an exception, and displays the error message as "Hi".

 

On running the above program, a mammoth dialog box pops up. We request you to click on the 'No' button, or else you could be transported to the Debugger.

 

a.vb

public class zzz

shared sub Main()

abc

System.Console.WriteLine("After abc")

end sub

shared sub abc

dim a as System.Exception

a = new System.Exception("hi")

Throw a

System.Console.WriteLine("After throw")

end sub

end class

 

Error

Unhandled Exception: System.Exception: hi

   at zzz.abc()

   at zzz.Main()

 

The above example sheds some more light on the concept of Exception Handling. The main sub has a shared sub named abc, whose sole task is to throw an exception.

 

The output displayed validates the fact that a part of the code can throw exceptions too. Since the sub abc throws an exception, the output points to the sub. However, since the sub abc is called from Main sub, Main also gets displayed.

 

Thus, whenever we come across multiple lines referring to an exception, we have to read them in a specific sequence, i.e. front to back. The closest sub is the one that is responsible for throwing the exception, and the other subs are the ones that contain the previous subs.

 

Here, a point to be borne in mind is that the program comes to a grinding halt when an exception gets thrown. As a consequence of this, none of the code, either in the sub abc or in the sub main, gets called thereafter, and the application comes to an end.

 

Under certain situations, this type of abrupt ending of the program is totally unacceptable, since any sub called from any of the classes could throw an exception and bring the program to a standstill.

 

The next program reveals as to how this can be prevented from occurring.

 

a.vb

public class zzz

shared sub Main()

try

System.Console.WriteLine("Before abc")

abc

System.Console.WriteLine("After abc")

catch

System.Console.WriteLine("In Catch")

end try

System.Console.WriteLine("After try")

end sub

shared sub abc

dim a as System.Exception

a = new System.Exception("hi")

Throw a

System.Console.WriteLine("After throw")

end sub

end class

 

Output

Before abc

In Catch

After try

 

The above program neither displays any exception dialog box, nor does it quit on encountering the 'throw' statement. It completes execution upto the very end.

 

The first WriteLine function executes normally, followed by the call to the sub abc. Thereafter, the sub throws an exception, as a result of which, all the lines of code after the 'throw' get ignored.

 

The significant point here is that, the sub abc has been placed in a try-catch clause, which is why the dialog box does not get displayed. Instead, the program executes code in the 'catch', and then, moves out gracefully at the end try statement. No code gets called after the sub abc has been called.

 

Thus, by placing all the exception handling code within the catch, all the probable exceptions can be suitably catered for. The code that is to be placed in the catch block is left entirely to your discretion.

 

Depending upon the situation, you may either place all the subs and functions in one gigantic 'try and catch' statement, or place them in individual 'try and catch' statements. Visual Basic.Net has no rules in this regard.

 

a.vb

public class zzz

dim shared i as integer = 10

shared sub Main()

try

System.Console.WriteLine("Before abc")

abc

System.Console.WriteLine("After abc")

catch e as System.Exception when i <= 5

System.Console.WriteLine("In Catch")

end try

System.Console.WriteLine("After try")

end sub

shared sub abc

dim a as System.Exception

a = new System.Exception("hi")

Throw a

System.Console.WriteLine("After throw")

end sub

end class

 

Output

Before abc

 

Unhandled Exception: System.Exception: hi

   at zzz.abc()

   at zzz.Main()

 

When we run the above program, the offending dialog box gets displayed, as before. This happens because, in addition to the Exception argument, the 'catch' clause also requires a 'when' clause along with a condition. We have created a variable i and set its value to 10.

 

While catching the exception, the condition is evaluated. Since the condition results in a value of False, the catch is overlooked, thus leaving it to the system to handle the exception. It behaves as though the catch statement never existed.

 

a.vb

public class zzz

dim shared i as integer = 10

shared sub Main()

try

System.Console.WriteLine("Before abc")

abc

System.Console.WriteLine("After abc")

catch e as System.Exception when i <= 5

System.Console.WriteLine("In Catch i <= 5")

catch e as System.Exception when i > 5

System.Console.WriteLine("In Catch i > 5")

System.Console.WriteLine(e.ToString)

end try

System.Console.WriteLine("After try")

end sub

shared sub abc

dim a as System.Exception

a = new System.Exception("hi")

Throw a

System.Console.WriteLine("After throw")

end sub

end class

 

Output

Before abc

In Catch i > 5

System.Exception: hi

   at zzz.abc()

   at zzz.Main()

After try

 

The above program does not display any dialog boxes, since it contains two catch statements:

     The first one handles exceptions when the value of the variable i is less than 5.

     The second one caters to the exceptions when the value of i is greater than 5.

 

Thus, depending on the value of the variable i, one of the two catch statements gets called.

 

If we change the condition of the first catch to 'when i > 5', even though both the conditions are the same, Visual Basic.Net does not complain. This is because, the moment any one of the conditions gets satisfied, all the other conditions get ignored.

 

The string representation of the exception object is displayed using the sub ToString, which is similar to the manner in which error messages are displayed.

 

a.vb

public class zzz

dim shared i as integer = 10

shared sub Main()

try

System.Console.WriteLine("First try")

try

System.Console.WriteLine("Before abc")

abc

System.Console.WriteLine("After abc")

catch

System.Console.WriteLine("In second Catch")

end try

System.Console.WriteLine("After first try")

catch

System.Console.WriteLine("In First catch")

end try

System.Console.WriteLine("After second try")

end sub

shared sub abc

dim a as System.Exception

a = new System.Exception("hi")

Throw a

System.Console.WriteLine("After throw")

end sub

end class

 

Output

First try

Before abc

In second Catch

After first try

After second try

In the above program, there are two try statements, one ensconced within the other. In the first try statement, the WriteLine function gets executes. In the second try statement, the WriteLine function gets executes once again, without much ado!

 

The exception thrown by the sub abc is caught in the catch of the inner try statement. Once the catch completes execution of its code, the program moves from the inner try to the outer try. Since the exception has already been caught and handled by the inner try, the outer catch does not catch it again.

 

Thus, as long as one of the catch statements is able to catch the exception that has been thrown in the try, the other catch statements will behave as though no exception was thrown. This would occur even if the condition of the second try statement is satisfied.

 

Thus, as long as even one catch statement in the sequence catches the exception, none of the other catch statement gets called.

 

a.vb

public class zzz

shared sub Main()

try

abc

catch 

System.Console.WriteLine("In Catch ")

finally

System.Console.WriteLine("In Finally")

end try

System.Console.WriteLine("After try")

end sub

shared sub abc

dim a as System.Exception

a = new System.Exception("hi")

Throw a

System.Console.WriteLine("After throw")

end sub

end class

 

 

Output

In Catch

In Finally

After try

 

The 'finally' clause is an integral part of the 'try-catch-finally' statements. The exception that is thrown can be easily caught in one of the catch statements. However, when there are multiple catch statements, only one of them will get executed.

 

One problem that could befall us is that, there could be some common code that needs to be executed for all the catch statements. One solution is to repeat the same code in each 'catch' statement. The other option would be to use the 'finally' clause. After all the statements in the 'catch' block have been executed, just before the 'try' clause ends, the code placed in the 'finally' block gets called.

 

Now, comment out the line of code that calls the sub abc, thereby, evading all exceptions that get thrown. Thus, when the program is executed, no exceptions are thrown. Nevertheless, the code written in the 'finally' clause gets called.

 

a.vb

public class zzz

dim shared i as integer = 10

shared sub Main()

try

System.Console.WriteLine("First try")

 try

 System.Console.WriteLine("Before abc")

 abc

 System.Console.WriteLine("After abc")

 catch when i >= 100

 System.Console.WriteLine("In second Catch")

 finally

 System.Console.WriteLine("In second finally")

 end try

System.Console.WriteLine("After first try")

catch

System.Console.WriteLine("In First catch")

finally

System.Console.WriteLine("In first finally")

end try

System.Console.WriteLine("After second try")

end sub

shared sub abc

dim a as System.Exception

a = new System.Exception("hi")

Throw a

System.Console.WriteLine("After throw")

end sub

end class

 

Output

First try

Before abc

In second finally

In First catch

In first finally

After second try

 

This program initiates two new concepts. As before, there are two 'try' statements. The sub abc throws an exception, which the catch is unable to catch since the variable i has a value less than 100, thereby causing the condition to evaluate to false. Now, the onus falls on the outer try to catch the exception.

 

However, before exiting the inner try, it first calls the 'finally' clause of the inner 'try-catch-finally' block, and then, it executes code in the outer 'catch' statement. Thereafter, it executes the 'finally' block of the outer 'catch' statement. In case, the outer 'catch' fails to catch the exception, the system will display the appalling dialog box, which has been shown earlier.

 

a.vb

public class zzz

shared sub Main()

try

abc

catch 

System.Console.WriteLine("In Catch Exception")

end try

end sub

shared sub pqr

dim a as System.Exception

a = new System.Exception()

Throw a

end sub

shared sub abc

pqr

end sub

end class

 

Output

In Catch Exception

 

This example is a variation of the above example. It proves that when the exception has to be caught, it moves up the sub hierarchy. Thus, the concept is that someone somewhere should be able to catch the exception.

 

The Main subroutine calls abc, which in turn calls pqr. The sub pqr throws the exception. Since there is no catch block in pqr, the catch within the sub abc should normally get called. However, there is no catch present in the abc block, due to which, the catch in the main function is evoked. The catch in the Main block displays 'In Catch Exception'.

 

However, if the sub Main too does not provide for the catch clause, the exception based dialog box will get displayed. Thus, the presence of at least one try catch in the Main subroutine is strongly prescribed.

 

a.vb

public class zzz

shared sub Main()

try

abc

catch 

System.Console.WriteLine("In Catch ")

exit try

System.Console.WriteLine("In Catch 1")

finally

System.Console.WriteLine("In Finally")

end try

System.Console.WriteLine("After try")

end sub

shared sub abc

dim a as System.Exception

a = new System.Exception("hi")

Throw a

System.Console.WriteLine("After throw")

end sub

end class

 

Output

In Catch

In Finally

After try

 

The program introduces the statement of 'Exit Try', which ceases to process all the 'catch' statements, and exits from the 'try' gracefully. Thus, the second occurrence of the WriteLine function does not get executed.

 

The job of Exit Try is to abandon the try statement instantly. However, the program is not permitted to sneak out of the try statement, without first paying homage to the finally clause. The output reveals that the code in the finally clause has been executed before the 'try' ended.

 

a.vb

public class zzz

shared sub Main()

try

abc

catch  e as System.Exception

System.Console.WriteLine("In Catch ")

end try

end sub

shared sub abc

dim a as xxx

a = new xxx()

Throw a

end sub

end class

class xxx

Inherits System.Exception

end class

 

Output

In Catch

 

An exception is simply any class that is derived from the exception class. The class xxx is an exception handling class, since it derives from the class exception. In the sub abc, an exception of type xxx is thrown. Note that it is not of type Exception.

 

However in the catch, the exception that is caught is of type Exception, and not of type xxx. This is absolutely permissible, since all exceptions get caught by the type Exception, since they derive from it.

 

a.vb

public class zzz

shared sub Main()

try

abc

catch  e as xxx

System.Console.WriteLine("In Catch xxx")

catch  e as System.Exception

System.Console.WriteLine("In Catch Exception")

end try

end sub

shared sub abc

dim a as xxx

a = new xxx()

Throw a

end sub

end class

class xxx

Inherits System.Exception

end class

 

Output

In Catch xxx

 

The appropriate approach to catch exceptions would be to check the exceptions that are thrown by a sub. The above program has only one sub abc, which throws only one exception of type xxx. However, if ten different types of exceptions are thrown, then good programming practice dictates that each of the ten exceptions should be caught using different catch statements.

 

Every catch should handle its exception individually. The exception that is not handled separately can be caught by the type Exception. Now, merely reverse the exceptions of the earlier program.

 

catch  e as System.Exception

System.Console.WriteLine("In Catch Exception")

catch  e as xxx

System.Console.WriteLine("In Catch xxx")

 

Output

In Catch Exception

 

This creates a quandary, since the second exception of type xxx shall never get caught. This is because, the first exception is of type Exception, which will catch all the exceptions.

 

a.vb

public class zzz

dim shared i as integer = 10

shared sub Main()

try

abc

catch when i <= 5

System.Console.WriteLine("In Catch Exception i <= 5")

catch e as xxx

System.Console.WriteLine("In Catch Exception xxx")

catch e as System.Exception when i > 100

System.Console.WriteLine("In Catch Exception i > 100")

catch e as System.Exception when i = 10

System.Console.WriteLine("In Catch Exception i = 10")

catch 

System.Console.WriteLine("In Catch Exception")

end try

end sub

shared sub abc

dim a as System.Exception

a = new System.Exception()

Throw a

end sub

end class

class xxx

Inherits System.Exception

end class

 

Output

In Catch Exception i = 10

 

The above example amply substantiates the fact that, a program can contain multiple catch statements, which can have a combination of both, the 'exception' and the 'when' clause. However, for the exception to be caught, both these conditions must be satisfied.

 

The first catch ascertains whether the value contained in variable i is less than or equal to 5.  Since i has been initialized to 10, the condition fails, thereby ignoring the catch for the exception. The second catch catches all exceptions of type xxx. The condition of this catch statement also does not get satisfied, since the exception thrown is of type Exception.

 

The third catch contains both, an Exception type, as well as a 'when' clause. The Exception to be caught is of type Exception, which evaluates to true. However, the when clause evaluates to false since i is not greater than 100. As a result, the catch does not get called. 

 

 

The last catch clause meets both the conditions. Therefore, the final outcome is a value of True. Thus, we can build as many conditions as we like in a 'catch' statement. However, for an exception to be caught, all the conditions must be met.

 

The rationale behind the concept of exception handling is that, it enables us to catch errors that occur during program execution while allowing the program to continue running. Thus, every program must have at least one try and catch encompassing the entire code, or else, the user may encounter the irksome dialog box, which could scare the wits out of him!

 

Keep in mind that even code written by Microsoft or other software companies could generate exceptions, which need to be caught.

 

Exceptions are deemed to be very useful in the context of constructors, since constructors are incapable of returning a value. Constructors contain considerable amount of code, and if any of the code fails, there is no mechanism by which the constructor can notify such failure. The best resolution of such a situation is to throw an exception.

 

Another convenience of exceptions is when multiple files are opened concurrently in an application. Each file requires almost identical error checks to be performed on it. No programmer of sound mind would want to write the same error check multiple times. Hence, he is bound to take the easy way out by refraining from including error checks altogether. This could result in fatal errors.

 

To avoid such a situation, a try-catch statement can be incorporated, which will ensure that all error handling code is placed in a single location. This simplifies the procedure of error checks while using multiple files, and it obviates the need to use the same code repetitively. 

 

Visual Basic supports two types of exceptions:

     Structured - which have been explicated earlier.

     Unstructured - which are so named due to their heritage.

 

 

The statement 'On Error' has been implemented. However, we will not be explaining this concept in this book.

 

The one thing to be borne in mind is that structured and unstructured exceptions cannot be used simultaeously in the program.

 

a.vb

public class zzz

dim shared i as integer = 10

shared sub Main()

try

abc

catch e as System.Exception

System.Console.WriteLine(e.HelpLink & "." + e.Message & "." + e.Source & "." + e.StackTrace  & "." & e.TargetSite.ToString)

end try

end sub

shared sub abc

dim a as xxx

a = new xxx("vijay")

Throw a

end sub

end class

class xxx

Inherits System.Exception

public sub new(i as string)

mybase.new(i)

end sub

end class

 

Output

.vijay.a.   at zzz.abc()

   at zzz.Main().Void abc()

 

In the above example, we have endeavoured to print out most of the properties of the Exception class.

 

The first property of HelpLink displays a null value. This property is used to locate the help file that provides greater details about the Exception that is thrown.

 

The next member is Message, which furnishes some more information about the Exception that was thrown. The message displayed is normally the string that is passed to the constructor. We have passed a value of 'vijay' to the xxx constructor. The class xxx is derived from the Exception class. The first line in the constructor of this class calls the base class and assigns it the string that has been passed to it. Due to this, the text 'vijay' gets displayed.

 

If you place the base class call within comments, the Message property will display 'Exception of type xxx was thrown'. This is the default string assigned to the Message property.

 

The a.exe application is executed. It has been created from the Visual Basic.Net source file named a.vb. Due to this, the Source property has been assigned the value of 'a'. When the Visual Basic.Net compiler is executed using the command vbc /out:b.exe a.vb, the name of the exe file changes to b.exe, thereby resulting in the Source property now displaying 'b'.

 

The StackTrace property is responsible for listing the subs that are responsible for throwing the exception. As mentioned earlier, this list has to be read in a specific sequence. The first line contains the sub that has thrown the exception. The subsequent subs are the ones that contain the preceding subroutine.

 

The TargetSite property is of type MethodBase, which represents a method that throws the exception. The ToString function displays the signature of the function.

 

A sub does not return a value, and hence, is not a function. The word Void denotes no return type. A function which has a return type of void, actually does not return any value at all. This makes the sub a function with a void return type. Subs are included in Visual Basic.Net because they were an integral part of the old VB language.