Overview
In Win32 API as well as COM, whenever an exceptional condition occurred during the execution of code, there was no explicit notification send to the caller to notify about the problem. Instead it was left to the caller to check if the call made was successful or not e.g. most Win 32 API functions return FALSE to indicate that something is wrong and then the caller needs to call GetLastError to find the details of the problem. And similarly in COM, if the first bit of HRESULT is 1 then the remaining bits would give you details of the violation. Thus it is up to the caller to make explicit checks to find out any violations and if the caller forgets to do so, the state of the application could be in-deterministic down the line.
.Net has changed the scenario around this. If any assumption is violated in a method, it throws an appropriate Exception which needs to be caught either in the calling method OR somewhere in the call stack. Else if there is no handler for the Exception, the CLR terminates the application rather than leaving it as it is with unpredictable results down the line.
How do you define Exception? Most common answer would be that Exception is an error or response to an error. That means if we are calling a method and the method throws an Exception, something went wrong in the execution of the method. To exemplify, let us say we have a method
Public Int Divide (int a, int b)
{
Return a/b;
}
Now if the caller of this method passed the value Zero or Null for b, the method would throw a DividebyZeroException (). Is this error due to some execution fault by the method OR incorrect value passed to it. Likewise CLR too throws some Exceptions like OutofMemoryException; StackOverflowException; etc which are not due to errors in code.
Exception handling in .Net
Exception handling in .Net is done via try...catch and finally blocks of code i.e. place the code that you expect or anticipate an exception in Try block and Catch those Exceptions in Catch block for the necessary action you may wish to take on those exceptions.
Finally block contains code (mostly some sort of clean up code) that is executed irrespective whether the Exception occurs in try block or no.
{
FileStream fs = null;
try {
fs = new FileStream(pathname, FileMode.Open)
……
}
catch (FileLoadException fle) {
//put the code here to handle FileLoadException or any exception derived from FileLoadException
}
catch (FileNotFoundException fnfe) {
//put the code here to handle FileNotFoundException or any exception derived from FileNotFoundException
}
catch (IOException ioe) {
//put the code here to handle IOException or any exception derived from IOException
}
catch (Exception e) {
//put the code here to handle any CLS compliant Exception.
}
catch {
//put the code here to handle any Exception, whether CLS compliant or not.
}
finally () {….
…..
}
}
The above shows a method which has a try block followed by few Catch blocks and one Finally block. Any Exception that originates in the Try block will be matched with the Catch blocks for the matching Exception or any of derivatives of the stated Exception. If not found, it goes to the next catch block and so on. After all the catch blocks are compared, if none caters to the Exception thrown then the Exception is passed to the next method up the call stack and the same procedure is repeated.
If any of the Catch blocks of the methods in the call stack can handle the thrown Exception, then all the Finally blocks from where the Exception was thrown up till the matching Catch block are executed and then the code in the matching Catch block filter is executed. Then the code in Finally block corresponding to the Catch block which handled the Exception is caught, is executed.
If the code in the Catch block does not throw/rethrow the exception and also no exception occurs in the code in the Finally block, the execution will fall to the code immediately after Finally block (or after Catch block, if there is no Finally block). Also if exception is thrown while executing code in Final block, it is treated as if thrown at end of the final block.
Catch blocks with specific exceptions need to be first, followed by the catch blocks with the generic exceptions. The reason for this is because if the catch block catch(Exception E) is the first one, then since every exception is derived from Exception class, this block would be the only one to catch all CLS compliant exceptions and none of the catch blocks below it would be ever executed.
Unfortunately it is a common practice to have catch blocks at end of most, if not all, methods.
Not only is this detrimental to performance but is also grossly incorrect and conceives the truth .
Exception handling should be put only in places where:
1. The exception can be handled by the code and efforts can be made to work around the
exception.
2. The exception needs to be wrapped into a more meaningful one and then re-thrown.
Example would be if a type provides facility of finding Phone Numbers to its users. Now if
the Phone numbers are being maintained in Files and if there is a FileException, it would
not be prudent to throw the file exception back to the user, as it may not make sense to
them. Better would be to wrap these exceptions in a more meaningful custom exception
and then re-throw it.
3. If a message is to be displayed to the user on occurrence of the Exception.
The Exception class from which all exception classes inherit has a public read-only property called StackTrace. Accessing this property actually access code in CLR; the property doesn’t simply return a string. If you create a Custom Exception derived from Exception and try to access this property, you will get NULL. When an Exception is thrown CLR records the point where the exception was thrown. When a catch filter accepts the thrown Exception, CLR records where the exception was caught. And now inside the catch filter if exception’s StackTrace property is accessed, the code implementing this property calls into the CLR which using the recorded start and end points of the thrown exception builds a string listing all the methods between the place where the exception was thrown to the place where it was caught.
Applying the attribute System.Runtime.CompilerServices.MethodImplAttribute on top of the method forbids the JIT complier from in lining the method for both debug and release builds.
All CLR compliant exceptions inherit from the class Exceptions. Initially Microsoft was advocating the strategy that all System Exceptions would inherit from the class System.Exception while all application Exception should inherit from ApplicationException. And both these exceptions would inherit from the base class Exception.
But in the course of building the FCL (Framework Class Library) Microsoft violated their own strategy, some reflection-related exception types are derived from ApplicationException instead of SystemException.
Checking against the ArguementException rather than each of the derived exceptions may be more helpful.
Exception class has 3 constructors, Blank; Accepting a string (which sets a descriptor) ; Accepting a string and an inner exception. In cases where the thrown exception is wrapped by a new or custom exception, the inner exception of this new exception class is set to the original thrown exception.
A custom exception can have its own data fields too besides the one provided by Exception class. But the caveat to watch here is that in case the Exception needs to be Serialized, serialization code for such additional fields in the custom exception classes needs to be coded. To provide serialize facilities to such custom exceptions class, annotate the class with Serialize attribute and also inherit the class from ISerialize interface and implement the methods for Serialization and Deserialization().
Exceptions which are not handled by the method propagate all the way up. These exceptions may be CLS compliant i.e. derived from Exception class or may be non-CLS compliant. These exceptions can be handled by attaching an event to AppDomain as shown below
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledExceptionCallbackMethod);
But if an application’s main thread or an unmanaged thread has an unhandled exception, IsTerminating will be true.
There is also Registry Entry whose value influences the handling of these Unhandled Exceptions. The registry entry is
HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework\ DbgJITDebugLaunchSetting.
Value 0: Displays a dialog box asking the user whether he would like to debug the process.
Value 1: No dialog box is displayed to the user and CLR fires the AppDomain’s UnhandledException event.
Value 2: No dialog box is displayed to the user and AppDomain’s UnhandledException event doesn’t fire. The CLR just spawns the debugger attaching it to the application.
To handle unhandled exceptions in Winforms define a method that matches the System.Threading.ThreadExceptionEventHandler delegate and register it with the Application type’s static ThreadException event.
Windows Forms deals only with CLS-compliant exceptions; non-CLS-compliant exceptions continue to propagate outside the thread’s message loop and up the call stack. To display or log both CLS-compliant and non-CLS-compliant exceptions, define two callback methods and register one with the Application type’s ThreadException event and register the other with AppDomain type’s UnhandledException event.
Unhandled exception in ASP.Net can be handled for a particular Web page or for all Webpages.
To register a callback method that will receive notifications for unhandled exceptions for each WebPage, register the callback method using the Error event offered by the System.Web.UI.TemplateControl class; this class is the base class of the System.Web.UI.Page and System.Web.UI.UserControl classes.
To register a callback method that will receive notifications of unhandled exceptions on any page, register the application-wide callback method using the Error event offered by the System.Web.HTTPApplication class in the Global.asax file.