Share via


Using Exceptions (C# Programming Guide) 

In C#, errors in the program at run time are propagated through the program using a mechanism called exceptions. Exceptions are thrown by code that encounters an error, and caught by code that can correct the error. Exceptions can be thrown by the .NET Framework common language runtime (CLR), or by code in a program. Once an exception is thrown, it propagates up the call stack until a catch statement for the exception is found. Uncaught exceptions are handled by a generic exception handler provided by the system that displays a dialog box.

Exceptions are represented by classes derived from Exception. This class identifies the type of exception, and contains properties with details about the exception. Throwing an exception involves creating an instance of an exception-derived class, optionally configuring properties of the exception, and then throwing the object with the throw keyword. For example:

private static void TestThrow()
{
    System.ApplicationException ex = 
        new System.ApplicationException("Demonstration exception in TestThrow()");

    throw ex;
}

After an exception is thrown, the runtime checks the current statement to see if it is within a try block. If so, any catch blocks associated with the try block are checked to see if they can catch the exception. Catch blocks normally specify exception types; if the type of the catch block is the same type as the exception, or a base class of the exception, the catch block can handle the method. For example:

static void TestCatch()
{
    try
    {
        TestThrow();
    }
    catch (System.ApplicationException ex)
    {
        System.Console.WriteLine(ex.ToString());
    }
}

If the statement that throws an exception is not within a try block, or the try block enclosing it has no matching catch block, the runtime checks the calling method for a try statement and catch blocks. The runtime continues up the calling stack, searching for a compatible catch block. After the catch block is found and executed, control is passed to the first statement after the catch block.

A try statement can contain more than one catch block. The first catch statement that can handle the exception is executed; any following catch statements, even if they are compatible, are ignored. For example:

static void TestCatch2()
{
    try
    {
        TestThrow();
    }
    catch (System.ApplicationException ex)
    {
        System.Console.WriteLine(ex.ToString());  // this block will be executed
    }
    catch (System.Exception ex)
    {
        System.Console.WriteLine(ex.ToString());  // this block will NOT be executed
    }

    System.Console.WriteLine("Done");  // this statement is executed after the catch block
}

Before the catch block is executed, the try blocks that have been evaluated by the runtime, including the try block containing the compatible catch block, are checked for finally blocks. Finally blocks allow the programmer to clean up any ambiguous state that could be left over from an aborted try block, or to release any external resources without waiting for the garbage collector in the runtime to finalize the objects. For example:

static void TestFinally()
{
    System.IO.FileStream file = null;
    System.IO.FileInfo fileInfo = new System.IO.FileInfo("C:\\file.txt");

    try
    {
        file = fileInfo.OpenWrite();
        file.WriteByte(0xF);
    }
    finally
    {
        // Closing the file allows you to reopen it immediately - otherwise IOException is thrown.
        if (file != null)
        {
            file.Close();
        }
    }

    try
    {
        file = fileInfo.OpenWrite();
        System.Console.WriteLine("OpenWrite() succeeded");
    }
    catch (System.IO.IOException)
    {
        System.Console.WriteLine("OpenWrite() failed");
    }

}

If WriteByte() threw an exception, the code in the second try block that attempts to reopen the file would fail if file.Close() is not called. Because finally blocks are executed whether or not an exception is thrown, the finally block in the previous example allows the file to be closed properly and helps avoid an error.

If no compatible catch block is found on the call stack after an exception is thrown, one of three things happens:

  • If the exception is within a destructor, the destructor is aborted and the base destructor, if any, is called.

  • If the call stack contains a static constructor, or a static field initializer, a TypeInitializationException is thrown, with the original exception assigned to the InnerException property of the new exception.

  • If the beginning of the thread is reached, the thread is terminated.

See Also

Reference

Exceptions and Exception Handling (C# Programming Guide)

Concepts

C# Programming Guide