Exception Handling: Catching and Handling Exceptions
Exception Handling: Catching and Handling Exceptions
Each exception in .NET contains the so-called stack trace, which gives
information of where exactly the error occurred.
Catching Exceptions
After a method throws an excpetion, CLR is looking for an exception
handler that can process the error. To understand how this works, we
need to take a closer look at the call stack.
try
{
//some code that may throw an excpeiton
}
catch(ExceptionType objectName)
{
// Code handling an Exception
}
catch(ExceptionType objectName)
{
//Code handling an exception
}
The try-catch constructs consists of one try block and one or more
catch blocks. Within the try block we put code that cold throw
excpetions. The ExceptionType in the catch block must be a type,
derived from System.Exception or the code wouldn't compile. the
expression within brackets after catch is also a declaration of a
variable, thus inside the catch block, we can use objectName to use
the properties of the excpetion or call its methods
The line numbers are included only if the respective class is compiled
with debug infromation(this information contains line numbers,
variable names and other technical information). The debug
information is not included in the .NET assemblies but is in separate
files called 'debug symbols'(.pdb). If the method thowing the exception
is a constructor, then instead of method name, the stack trace
contains the word .ctor.
The rich information in the stack allows us to quickly and easily find
the class, the method and even the source line where the error has
occured
Throwing Exceptions
Exceptions in C# are thrown using the keyword throw. We need to
provide an instance of the exception, containing all the necessary
information about the error. Exceptions are normal classes and the
only requirement is that they inherit direclty or indirecly from the
System.Exception class.
Exceptions Hierarchy
There are two types of exceptions in .NET Framework: exceptions
thrown by the applications we develop(ApplicationException) and
exceptions thrown by the runtime(SystemException). Each of these is
a base class for a hierarchy of exception classes.
the Exception Class
The Exception class contains a copy of the call-stack at the time the
exception instance was created. the class usually also has a short
message describing the error (filled in by the method throwing the
exception). Every exception could have a nested exception also
sometimes called an inner exception, wrapped exception or internal
exception.
try
{
// some code that could or could not cause an exception
}
finally
{
// Code here will always execute
}
Every try block may have zero or more catch blocks and at most one
finally block. It is possible to have multiple catch blocks and a finally
block in the same try-catch-finally constructs
try
{
//some code
}
catch (…)
{
// Code handling an exception
}
catch (…)
{
// Code handling another exception
}
finally
{
// This code will always execute
}
The previous example can be written in shorter form with the help of
the using keyword in C#
Use the using statement with all classes that implement the
IDidposable interface. When a class implements IDisposable interface
this means the creator of this class expects it can be used with the
using statement and the class contains some expensive resources
that should not be left unreleased. Implementing IDisposable also
means that it should be released immediately after we finnish using
the class and the easiest way to do this in C# is with using statement.
catch (IOException e)
The example above will catch not only the IOException, but all of its
descendants including FileNotFoundException,
EndOfStreamException, PathTooLongException and many others. In
the same time exceptions like UnauthorizedAccessException and
OutOfMemoryException will not be caught, because they don’t inherit
from IOException. We can look in MSDN for the exceptions hierarchy if
we wander which exceptions to catch. It is not a good practice, but it
is possible to catch all exceptions:
catch (Exception e)
Exceptions are confusing for most users. They give the impression of a
poorly written program that 'has bugs'. It is recommended when
exceptions are not caught by anyone(such exceptions can only be
runtime errors) to be caught by a global exception handler which
saves them on the disk and shows user friendly messages such as 'An
error occurred, please try again later'. It is a good practice to show not
only a user friendly message but also technocal information(stack
trace) available on demand.
When we catch an exception and throw a new one with a higher level
of abstarction, we should always attach the original exception to it.
This way the user of our code will be able to easily find the exact
reason for the error and the location where it occurred at the first
place. Each exception should carry detailed information about the
problem. From the rule above many rules come out: we should have a
relevant error message, the error type should match the problem and
the exceptions should hold its source as inner exception.