Saturday, March 02, 2024 00:31

Table of contents >> Debugging And Error Handling > Handling exceptions with Try Catch

Handling exceptions with Try Catch

Usually, when an exception is thrown, the program crashes or begins to malfunction. Fortunately, there is a way to prevent this that allows us to run codes that may generate exceptions, and still safely continue the execution in case of an error. The programming construct that offers us this fail safe is called a Try Catch block. The basic structure of a Try Catch block looks like this:

As you can see, a Try Catch block is composed of two parts: a try block, where we put the codes that might generate exceptions, and one or more Catch block(s), where we handle the exceptions that were caught. ExceptionType must be any type derived from System.Exception namespace, or the program will not compile. You can find some of these types in the lesson about exceptions. Because objectName is a variable just like any other variable, we can use it inside the Catch block to get information related to the caught error.

In the previous lesson, we had this code, that was trying to read a file that was not existing on the disk:

And you did see that the code generated an exception when we tried to run it. Let’s now modify it so that it uses a Try Catch block and handles the errors. To do this, all we need to do is wrap the code inside the ReadFile() method inside a Try block, and handle the exceptions that may arise in its Catch block.

By doing so, our program will work a bit different. First, because we wrapped the code inside the Try Catch block, when the StreamReader will be initialized with a filename that does not exist, it will no longer make our program crash. Instead, as soon as the Try block detects that an instruction thrown an exception, it will stop the execution inside its block (the remaining code inside the Try block will NOT be executed, after some instruction generated an exception) and transfer it immediately to the Catch block(s). At this point, the compiler will look up what types of exceptions are we handling inside our Catch block(s). If one of the them manages the kind of exception that was thrown, the execution will be transferred to that particular Catch block, where it will run all the code inside it.

In our case, because the StreamReader constructor will throw a FileNotFoundException, the execution will be transferred to the Catch block that handles FileNotFoundException exception types. If the exception would have been of any other type (for instance, file already in use, or insufficient permission to access it), the .NET runtime would look for a different Catch block that would handle that particular exception type, or a type from which the thrown exception is thrown. This is because exceptions are classes, which can be organized in hierarchies, and can inherit one from another (we haven’t learned about inheritance yet); thus, FileNotFoundException is an exception derived from IOException, which is also derived from ApplicationException, which is also derived from Exception, etc. If we didn’t specify a FileNotFoundException handler, but we specified a IOException handler, we would still be fine in case of an exception raised when we try to access a file which does not exist. This happens because IOException is an exception class that is “higher” in the hierarchy compared to the FileNotFoundException one, and FileNotFoundException inherits from it. So, we can say that because other exceptions inherit from IOException, IOException is able to handle more than one kind of exception, just like the Exception class (being the root type of all other types of exceptions, at the very top of the hierarchy) can handle any kind of exception.

So, in our case, since the problematic piece of code raised an exception of type FileNotFoundException and since we declared a Catch block that handles this kind of exceptions, the execution will be transferred immediately to this particular Catch block. As a parameter to this Catch construct, we see that the have a variable called fnfe, of type FileNotFoundException. Just like in a normal function, we can use this parameter variable inside the Catch block:

In the above code, when the FileNotFoundException exception is thrown, we are using the fnfe variable to print its message on the console using the ToString() method. Be aware, though, this is not considered a good programming practice, and I only exemplified it just so you see that the variable fnfe can be used inside the Catch block. Aside of this, you notice that inside the ReadFile() method we are only handling FileNotFoundException type of exceptions. If ANY other kind of exception would be thrown, it will be passed back to the Main() method, which, also having a Try Catch block that handles the Exception class directly (remember, the Exception class is at the top of hierarchy, it is the parent of all other exceptions, and thus, catches any kind of exception), handles the exception returned by the ReadFile() method. In spite of that, as we will see in a future lesson, using the Exception type directly to catch any kind of exception is also considered a bad programming practice.

Tags: , , ,

Leave a Reply

Follow the white rabbit