Microsoft.NET

……………………………………………….Expertise in .NET Technologies

An Overview of Exception Handling in C#

Posted by Ravi Varma Thumati on May 11, 2009

Try Statements and Exceptions

A try statement specifies a code block subject to error-handling or clean-up code. The try block must be followed by a catch block, a finally block, or both. The catch block executes when an error occurs in the try block. The finally block executes after execution leaves the try block (or if present, the catch block), to perform clean-up code, whether or not an error occurred.

A catch block has access to an Exception object, which contains information about the error. You use a catch block to either compensate for the error or re-throw the exception. You re-throw an exception if you merely want to log the problem, or if you want to re-throw a new, higher-level exception type.

A finally block adds determinism to your program by always executing no matter what. It’s useful for clean-up tasks such as closing network connections.

A try statement looks like this:

               try
               {
                 ... // exception may get thrown within execution of
                     // this block
               }
               catch (ExceptionA ex)
               {
                 ... // handle exception of type ExceptionA
               }
               catch (ExceptionB ex)
               {
                 ... // handle exception of type ExceptionB
               }
               finally
               {
                 ... // clean-up code
               }

 Consider the following program:

               static void Main()
               {
                 int x = 3, y = 0;
                 Console.WriteLine (x / y);
              
               }

y is zero, so the runtime throws a DivideByZeroException, and our program terminates. We can prevent this by catching the exception as follows:

               static void Main( )
               {
                 try
                 {
                   int x = 3, y = 0;
                   Console.WriteLine (x / y);
                 }
                 catch (DivideByZeroException ex)
                 {
                   Console.WriteLine ("y cannot be zero");
                 }
                 Console.WriteLine ("program completed");
               } 
               OUTPUT:
               x cannot be zero
               program completed

When an exception is thrown, the CLR performs a test:

Is execution currently within a try statement that can catch the exception?

  • If so, execution is passed to the compatible catch block. If the catch block successfully finishes executing, execution moves to the next statement after the try statement (if present, executing the finally block first).
  • If not, execution jumps back to the caller of the function, and the test is repeated (after executing any finally blocks that wrap the statement).

If no function takes responsibility for the exception, an error dialog is displayed to the user, and the program terminates.

The catch Clause

A catch clause specifies what type of exception to catch. This must either be System.Exception or a subclass of System. Exception.

Catching System.Exception catches all possible errors. This is useful when:

  • Your program can potentially recover, regardless of the specific exception type.
  • You plan to rethrow the exception (perhaps after logging it).
  • Your error handler is the last resort, prior to termination of the program.

More typically, though, you catch specific exception types to avoid having to deal with circumstances for which your handler wasn’t designed (e.g., an OutOfMemoryException).

You can handle multiple exception types with multiple catch clauses:

               try
               {
                 DoSomething();
               }
               catch (IndexOutOfRangeException ex) { ... }
               catch (FormatException ex)          { ... }
               catch (OverflowException ex)        { ... }

 Only one catch clause executes for a given exception. If you want to include a safety net to catch more general exceptions (such as System.Exception), you must put the more specific handlers first.

An exception can be caught without specifying a variable, if you don’t need to access its properties:

               catch (StackOverflowException) // no variable
                { ... }

 Furthermore, you can omit both the variable and the type (meaning that all exceptions will be caught):

               catch { ... }

The finally Block

A finally block always executes—whether or not an exception is thrown, and whether or not the try block runs to completion. finally blocks are typically used for cleanup code.

A finally block executes:

  • After a catch block finishes
  • After control leaves the try block because of a jump statement (e.g., return or goto)
  • After the try block ends

A finally block helps add determinism to a program. In the following example, the file that we open always gets closed, regardless of whether:

  • The try block finishes normally.
  • Execution returns early, as the file is empty (EndOfStream).
  • An IOException is thrown while reading the file.
               static void ReadFile()
               {
                 StreamReader reader = null; // In System.IO
                 try
                 {
                   reader = File.OpenText ("file.txt");
                   if (reader.EndOfStream) return;
                   Console.WriteLine (reader.ReadToEnd());
                 }
                 finally
                 {
                   if (reader != null) reader.Dispose();
                 }
               }

 In this example, we closed the file by calling Dispose on the StreamReader. Calling Dispose on an object, within a finally block, is a standard convention throughout the .NET Frame-work and is supported explicitly in C# through the using statement.

The using statement

Many classes encapsulate unmanaged resources, such as file handles, graphics handles, or database connections. These classes implement System.IDisposable, which defines a single parameterless method named Dispose to clean up these resources. The using statement provides an elegant syntax for instantiating an IDisposable object and then calling its Dispose method within a finally block.

The following:

               using (StreamReader reader = File.OpenText (
                      "file.txt"))
               {
                 ...
               }

 is precisely equivalent to:

               StreamReader reader = File.OpenText ("file.txt");
               try
               {
                 ...
               }
               finally
               {
                 if (reader != null)
                  ((IDisposable)reader).Dispose();
               }

Throwing Exceptions

Exceptions can be thrown either by the runtime, or in user code. In this example, Display throws a System. ArgumentNullException:

               static void Display (string name)
               {
                 if (name == null)
                   throw new ArgumentNullException ("name");
                 Console.WriteLine (name);
               }
               static void Main()
               {
                 try { Display (null); }
                 catch (ArgumentNullException ex)
                 {
                   Console.WriteLine ("Caught the exception");
                 }
               }

Re-throwing an exception

You can capture and rethrow an exception as follows:

               try {  ...  }
               catch (Exception ex)
               {
                 // Log error
                 ...
                 throw;          // Rethrow same exception
               }

 Rethrowing in this manner lets you log an error without swallowing it. It also lets you back out of handling an exception should circumstances be outside what you expected.

The other common scenario is to rethrow a more specific exception type. For example:

               try
               {
                 ... // parse a date of birth from XML element data
               }
               catch (FormatException ex)
               {
                 throw new XmlException ("Invalid date of birth", ex);
               }

 Rethrowing an exception does not affect the StackTrace property of the exception (see the next section). When rethrowing a different exception, you can set the InnerException property with the original exception if doing so could aid debugging. Nearly all types of exceptions provide a constructor for this.

Key Properties of System.Exception

The following are the most important properties of System. Exception:

StackTrace

A string representing all the methods that are called from the origin of the exception to the catch block.

Message

A string with a description of the error.

InnerException

The inner exception (if any) that caused the outer exception. This, itself, may have another InnerException.

Common Exception Types

The following exception types are used widely throughout the CLR and .NET Framework. You can throw these yourself, or use them as base classes for deriving custom exception types.

  • System.ArgumentException

Thrown when a function is called with a bogus argument. This generally indicates a program bug.

  • System.ArgumentNullException

Thrown when a function argument is (unexpectedly) null. (It is a subclass of ArgumentException.

  • System.ArgumentOutOfRangeException

Thrown when a (usually numeric) argument is too big or too small. (It is also a subclass of ArgumentException.) For example, this is thrown when passing a negative number into a function that accepts only positive values.

  • System.InvalidOperationException

Thrown when the state of an object is unsuitable for a method to successfully execute, regardless of any particular argument values. Examples include reading an unopened file or getting the next element from an enumerator where the underlying list has been modified partway through the iteration.

  • System.NotSupportedException

Thrown to indicate that a particular functionality is not supported. A good example is calling the Add method on a collection for which IsReadOnly returns true.

  • System.NotImplementedException

Thrown to indicate that a function has not yet been implemented.

  • System.ObjectDisposedException

Thrown when the object upon which the function is called has been disposed.

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: