Exceptions and Exception handling in C#
The C#
language's exception handling features help you deal with any unexpected or
exceptional situations that occur when a program is running. Exception handling
uses the
try
, catch
,
and finally
keywords
to try actions that may not succeed, to handle failures when you decide that it
is reasonable to do so, and to clean up resources afterward. Exceptions can be
generated by the common language runtime (CLR), by the .NET Framework or any
third-party libraries, or by application code. Exceptions are created by using
the throw
keyword.
In many cases, an exception may be thrown not by a method that
your code has called directly, but by another method further down in the call
stack. When this happens, the CLR will unwind the stack, looking for a method
with a
catch
block
for the specific exception type, and it will execute the first such catch
block
that is found. If it finds no appropriate catch
block
anywhere in the call stack, it will terminate the process and display a message
to the user.
C# includes built-in classes
for every possible exception. All the exception classes are directly or
indirectly derived from the Exception class.
There are two main classes for exceptions - SystemException and ApplicationException.
SystemException is a base class for all CLR generated errors whereas
ApplicationException serves as a base class for all application related
exceptions, which you want to raise on business rule violation.
Exception classes Hierarchy
As you can see in the above figure, SystemException class is
a base class for all the exception that can occurs during execution of the
program. No other class derives ApplicationException class by default, because
you as a programmer need to derive this class to create your own exeception
classes as per the business rules.
Important Exception Classes
The following table lists important exception classes
available in .Net.
Exception |
Description |
ArgumentException |
Raised when
a non-null argument that is passed to a method is invalid. |
ArgumentNullException |
Raised
when null argument is passed to a method. |
ArgumentOutOfRangeException |
Raised when
the value of an argument is outside the range of valid values. |
DivideByZeroException |
Raised
when an integer value is divide by zero. |
FileNotFoundException |
Raised when
a physical file does not exist at the specified location. |
FormatException |
Raised
when a value is not in an appropriate format to be converted from a string by
a conversion method such as Parse. |
IndexOutOfRangeException |
Raised when
an array index is outside the lower or upper bounds of an array or collection. |
InvalidOperationException |
Raised
when a method call is invalid in an object's current state. |
InvalidCastException |
Raised when
incompitible types are being converted. |
KeyNotFoundException |
Raised
when the specified key for accessing a member in a collection is not exists. |
NotSupportedException |
Raised when
a method or operation is not supported. |
NullReferenceException |
Raised
when program access members of null object. |
OverflowException |
Raised when
an arithmetic, casting, or conversion operation results in an overflow. |
OutOfMemoryException |
Raised
when a program does not get enough memory to execute the code. |
StackOverflowException |
Raised when
a stack in memory overflows. |
TimeoutException |
The time
interval allotted to an operation has expired. |
Every exception class in .Net is derived from the base
Exception class. It includes the following important properties using which you
can use to get information about the exception when you handle the exception.
Property |
Description |
Message |
Provides details about the cause of the exception. |
StackTrace |
Provides information about where the error occurred. |
InnerException |
Provides information about the series of exceptions that
might have occurred. |
HelpLink |
This property can hold the help URL for a particular
exception. |
Data |
This property can hold arbitrary data in key-value pairs. |
TargetSite |
Provides the name of the method where this exception was
thrown. |
When an error occurs, either application code
or the default handler handles the exception.
The Anatomy of C# Exceptions
Exceptions allow an
application to transfer control from one part of the code to another. When an
exception is thrown, the current flow of the code is interrupted and handed
back to a parent try catch block. C# exception handling is done with the follow
keywords: try, catch, finally, and throw
·
try – A try block is used to encapsulate a
region of code. If any code throws an exception within that try block, the
exception will be handled by the corresponding catch.
·
catch – When an exception occurs, the Catch
block of code is executed. This is where you are able to handle the exception,
log it, or ignore it.
·
finally – The finally block allows you to execute
certain code if an exception is thrown or not. For example, disposing of an
object that must be disposed of.
·
throw – The throw keyword is used to actually
create a new exception that is the bubbled up to a try catch finally block.
C# try and catch
The
try
statement allows you to define a block of code to be tested
for errors while it is being executed.
The
catch
statement allows you to define a block of code to be
executed, if an error occurs in the try block.
The
try
and catch
keywords come in pairs:Syntax
public static void Main()
{
try
{
// Block of code to try
}
catch (Exception e)
{
// Block of code to handle errors
}
}
Consider the following example, where we create an array of
three integers:
The code snippet below will generate an error, because
numArray[5] does not exist.
int[] numArray = { 1, 2, 3 };
Console.WriteLine(numArray[5]); //
error!
The error message will be something like shown below:
System.IndexOutOfRangeException:
'Index was outside the bounds of the array.'
If an error
occurs, we can use
try...catch
to catch the error and execute some code to handle it.
In the following example, we
use the variable inside the catch block (
e
)
together with the built-in Message
property, which outputs a message that describes the
exception:
public static void Main()
{
try
{
int[] myNumbers = { 1, 2, 3 };
Console.WriteLine(myNumbers[5]);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
Finally
The
finally
statement lets you execute code, after try...catch
, regardless of the result:
public static void Main()
{
try
{
int[] myNumbers = { 1, 2, 3 };
Console.WriteLine(myNumbers[5]);
}
catch (Exception e)
{
Console.WriteLine("Something went wrong.");
}
finally
{
Console.WriteLine("The 'try catch' is finished.");
}
}
The throw keyword
The
throw
statement allows you to create a custom error.
The
throw
statement is used together with an exception class. There are many exception
classes available in C#: ArithmeticException
, FileNotFoundException
, IndexOutOfRangeException
, TimeOutException
, etc:
Example
static void checkAge(int age)
{
if (age < 18)
{
throw new ArithmeticException("Access denied - You must be at least 18 years old.");
}
else
{
Console.WriteLine("Access granted - You are old enough!");
}
}
Performance considerations
Throwing
or handling an exception consumes a significant amount of system resources and
execution time. Throw exceptions only to handle truly extraordinary conditions,
not to handle predictable events or flow control. For example, in some cases,
such as when you're developing a class library, it's reasonable to throw an
exception if a method argument is invalid, because you expect your method to be
called with valid parameters. An invalid method argument, if it is not the
result of a usage error, means that something extraordinary has occurred.
Conversely, do not throw an exception if user input is invalid, because you can
expect users to occasionally enter invalid data. Instead, provide a retry
mechanism so users can enter valid input. Nor should you use exceptions to
handle usage errors. Instead, use assertions to
identify and correct usage errors.
In addition, do not throw an exception when a return code is
sufficient; do not convert a return code to an exception; and do not routinely
catch an exception, ignore it, and then continue processing.
Re-throwing an exception
In
many cases, an exception handler simply wants to pass the exception on to the
caller. This most often occurs in:
·
A
class library that in turn wraps calls to methods in the .NET Framework class
library or other class libraries.
·
An
application or library that encounters a fatal exception. The exception handler
can log the exception and then re-throw the exception.
The recommended way to re-throw an exception is to simply use
the throw statement
in C# without including an expression. This ensures that all call stack
information is preserved when the exception is propagated to the caller. The
following example illustrates this. A string extension method,
FindOccurrences
,
wraps one or more calls to String.IndexOf(String,
Int32) without validating its arguments beforehand.Example
using System;
using System.Collections.Generic;
public static class Library
{
public static int[] FindOccurrences(this String s, String f)
{
var indexes = new
List<int>();
int currentIndex = 0;
try
{
while (currentIndex >= 0 && currentIndex < s.Length)
{
currentIndex = s.IndexOf(f,
currentIndex);
if (currentIndex >= 0)
{
indexes.Add(currentIndex);
currentIndex++;
}
}
}
catch (ArgumentNullException)
{
// Perform some
action here, such as logging this exception.
throw;
}
return indexes.ToArray();
}
}
Caller
public class Example
{
public static void Main()
{
String s = "It was a cold day when...";
int[] indexes = s.FindOccurrences("a");
ShowOccurrences(s, "a", indexes);
Console.WriteLine();
String toFind = null;
try
{
indexes =
s.FindOccurrences(toFind);
ShowOccurrences(s, toFind,
indexes);
}
catch (ArgumentNullException e)
{
Console.WriteLine("An exception ({0}) occurred.",
e.GetType().Name);
Console.WriteLine("Message:\n
{0}\n", e.Message);
Console.WriteLine("Stack Trace:\n
{0}\n", e.StackTrace);
}
}
private static void ShowOccurrences(String s, String
toFind, int[] indexes)
{
Console.Write("'{0}' occurs at the following character positions: ",
toFind);
for (int ctr =
0; ctr < indexes.Length; ctr++)
Console.Write("{0}{1}", indexes[ctr],
ctr == indexes.Length
- 1 ? "" : ", ");
Console.WriteLine();
}
}
A caller then calls
FindOccurrences
twice. In the second call to FindOccurrences
, the caller passes a null
as the search string, which cases the String.IndexOf(String, Int32) method
to throw an ArgumentNullException exception.
This exception is handled by the FindOccurrences
method and passed back to the caller. Because the throw
statement is used with no expression, the output from the example shows that
the call stack is preserved.References
1.
W3schools
4.
CsharpCorner
Comments
Post a Comment