try Statements and Exceptions

A try statement specifies a code block subject to error-handling or cleanup 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 cleanup code, whether or not an error occurred.

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

A finally block adds determinism to your program, by always executing no matter what. It’s useful for cleanup 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 code:

int x = 3, y = 0;
Console.WriteLine (x / y);

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

try
{
  int x = 3, y = 0;
  Console.WriteLine (x / y);
}
catch (DivideByZeroException ex)
{
  Console.Write ("y cannot be zero. ");
}
// Execution resumes here after exception...

Note

This is a simple example to illustrate exception handling. We could deal with this particular scenario better in practice by checking explicitly for the divisor being zero before calling Calc.

Exceptions are relatively expensive to handle, taking hundreds of clock cycles.

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 in the call stack takes responsibility for the exception, an error dialog is displayed to the user, and the program terminates.

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:

More typically, though, you catch specific exception types, in order 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 { ... }

Note

In languages other than C#, it is possible (though not recommended) to throw an object that does not derive from Exception. The CLR automatically wraps that object in a RuntimeWrappedException class (which does derive from Exception).

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 either:

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

For example:

static void ReadFile()
{
  StreamReader reader = null;  // In System.IO namespace
  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 Framework and is supported explicitly in C# through the using statement.

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);
}

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

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.