Failing to prepare is preparing to fail. Ergo, one is constantly planning and preparing for the future. We often set a path for ourselves and try to follow it steadily. But then life intervenes. Uncertainties in life result in unforeseen situations. These are like exceptions in the normal course that we set for ourselves.
Similarly, when you write programs, unforeseen problems may arise during its normal path of execution.
These unforeseen problems are nothing but an euphemism for errors. Just as in life, in the programming world, these errors can be further classified into Fatal errors and Non-Fatal errors. A Fatal error is an error that brings the program to a grinding halt. A Non-Fatal error is an error that allows your program to run but with limited capacity. This can be exemplified by the following.
Let's assume you have a card that is not of a high resolution. Accordingly, your browser displays your page in a lower resolution. Now, technically, that is an error but it is not a Fatal one. However, if you didn't have a graphics card at all then it would be a Fatal error. Thus, we may also call an unforeseen problem or error an Exception. In other words, therefore, the word Exception is used almost synonymously with the word Error.
Earlier, the problem was that we never centralized error handling. Let's assume you have to open three files. Each time you open a file you have to check whether an error occurred or not. So you have to conduct that check for every file. Since there are three files, it would mean repeating the same error check thrice. That is surely a waste of time. Or you could be calling two functions and checking for the same error in both the functions. One reason that programmers don't write error-handling routines is that they get tired of the mundane task. It is the same thing repeated over and over again.
Let's consider constructors. Before the constructor gets called, the object has not yet been created. So you ask the constructor to create an object, to allocate memory and create a file. Now, if it can't do so, how will the constructor return to tell you that an error occurred! Today constructors carry a lot of code within them and if you haven't forgotten, constructors cannot return values.
Because of the various reasons discussed above, we don't talk about errors any more; we handle exceptions. Bearing this in mind let's understand the next program.
a.cs class zzz {
public static void Main() {
yyy a;
a=new yyy();
a.abc();
System.Console.WriteLine("Bye");
} }
class yyy {
public void abc() {
throw new System.Exception();
System.Console.WriteLine("abc");
} }
Compiler Warning
a.cs(16,1): warning CS0162: Unreachable code detected Output
Unhandled Exception: System.Exception: Exception of type System.Exception was thrown at yyy.abc()
at zzz.Main()
Here, a.abc calls the function abc in class yyy. System.Console.WriteLine is used to display 'Bye'. It is of significance to note that when you run this program the System.Console.WriteLine does not get called. Hence the word 'Bye' is not displayed. Within the abc function we have a line that says throw new System.Exception(); The word new indicates that we are creating an object. We are creating an object that looks like System.Exception. 'throw' is a reserved word, that means it is recognized by C#.
Exception is a class in the System namespace. In other words, we are identifying an exception, creating an object of it, and throwing it. Then we have a WriteLine statement for printing 'abc'. Note that neither 'Bye' nor 'abc' gets displayed. A Message Box may appear for debugging the applicaition. Since we are still at the learning stage, we click on the No button.
The warning says that when you use the 'throw' keyword in your code, no lines of code get called after that. Since the function abc is throwing an exception no code after the throw in abc will get executed.
The throw acts like the return statement. Everything comes to a stand still! And we get an error at runtime and not at the time of running the compiler; indicating where the exception occurred. Also no code gets called after function abc gets called as it throws an exception.
Explicitly declaring exceptions tells the compiler that a particular problem might occur. When the problem does occur, an exception is thrown; the next step being to catch the exception. Let's see how we can accomplish this. In our program, the function abc throws an exception. We will now catch the exception.
a.cs class zzz {
public static void Main() {
yyy a;
a=new yyy();
try { a.abc();
System.Console.WriteLine("Bye");
}
catch (System.Exception e) {
System.Console.WriteLine("In Exception"+ e.ToString());
}
System.Console.WriteLine("After Exception");
} }
class yyy {
public void abc() {
throw new System.Exception();
System.Console.WriteLine("abc");
} } Output
In ExceptionSystem.Exception: Exception of type System.Exception was thrown.
at yyy.abc() at zzz.Main() After Exception
Catching exceptions is done within 'try-catch' blocks. Therefore, the code for abc is included within a 'try-catch' block. a.abc - the function that throws the exception - is included within the try-catch block . The abc function throws an exception by using the keyword throw. There is no other way of throwing an exception. At this point all code is skipped in function abc as well as the in the try block and the control moves to the catch block. As such, neither 'abc' nor 'bye' gets displayed.
Within the catch we have a parameter 'e' that looks like System.Exception. The object e has a method called ToString. ToString is a very handy function. It tells you where exactly the exception occurred and in which function, function within function. So, System.Console.Writeline will display the string 'In Exception' along with the exception.
After the code contained in the catch block is executed, the remaining code after the end of the try - catch block will be executed. Hence, WriteLine will display 'After Exception'. That means the program will not come to a stand still, it resumes execution after the catch and not after the function which threw the exception. If you give a return statement immediately after the catch block, as we have given in the next program, the program will stop execution there itself. Hence, in this case, 'After Exception' will not be displayed as shown below.
a.cs class zzz {
public static void Main() {
yyy a;
a=new yyy();
try { a.abc();
System.Console.WriteLine("Bye");
}
catch (System.Exception e) {
System.Console.WriteLine("In Exception"+ e.ToString());
return;
}
System.Console.WriteLine("After Exception");
} }
class yyy {
public void abc() {
throw new System.Exception();
System.Console.WriteLine("abc");
} }
Output
In ExceptionSystem.Exception: Exception of type System.Exception was thrown.
at yyy.abc() at zzz.Main()
Each time that abc gets called an exception is thrown. But you may not want that to happen. Hence, exception handling is normally included in an if statement and if an error condition takes place.
A 'try-catch' block can include a number of functions and whenever an exception occurs for any one of them, we will catch it. By doing so we are synchronizing all the code to handle errors at one place.
Constructors can also throw exceptions.