Skip to content
Theory · Exceptions · lesson 2 of 9

try and catch

Let's go back to the fire alarm. Say one of the floors has a guard on duty. The guard hears the signal, deals with it, and the alarm goes no further: the building works as before. In C#, you place such a guard right in the code, and it is called a catch block.

The analogy is off in one respect. After dealing with the alarm, the guard sends people back to their desks. After a catch, the program does not go back to the place of the error: the lines of the try block below that place never run.

How try and catch work

Code that may throw an exception goes into a try block, followed right away by a catch block:

C#
Console.WriteLine("Start"); // Start
try
{
    int n = int.Parse("ten");
    Console.WriteLine(n);
}
catch (FormatException ex)
{
    Console.WriteLine("Error: " + ex.Message); // Error: The input string 'ten' was not in a correct format.
}
Console.WriteLine("End"); // End

Let's go through the program step by step:

  1. Start is printed as usual.
  2. The try block holds, in its braces, the code that may fail.
  3. int.Parse("ten") throws a FormatException. The rest of the try block is skipped: Console.WriteLine(n) does not run.
  4. The block catch (FormatException ex) waits for exceptions of the class FormatException. The class matches, and the block prints the message.
  5. After the catch, the program goes on and prints End. The exception has been handled, and the program did not stop.

A catch block is called an exception handler. When a handler runs, it is said to have caught the exception.

If you replace "ten" with "42", there is no exception. The try block runs to the end and prints 42, and the catch is skipped.

The exception variable

The parentheses after catch hold a class and a variable name: FormatException ex. The variable ex holds the very exception object that .NET created. Its Message property is the message that came after the colon in the report.

If you don't need the object, you can leave the name out: catch (FormatException). The block still catches exceptions of that class.

Several catch blocks

One try can have several catch blocks. When an exception happens, they are checked from top to bottom, and the first one whose class matches runs. The rest are skipped:

C#
string text = "0";
try
{
    Console.WriteLine(100 / int.Parse(text));
}
catch (FormatException)
{
    Console.WriteLine("Not a number");
}
catch (Exception ex)
{
    Console.WriteLine("Error: " + ex.Message); // Error: Attempted to divide by zero.
}

What the program prints depends on text:

  • "0": dividing by zero throws a DivideByZeroException. The first block waits for FormatException and does not match; the second prints Error: Attempted to divide by zero.
  • "ten": int.Parse throws a FormatException, and the first block prints Not a number.
  • "8": there is no exception, the program prints 12, and both blocks are skipped.

catch (Exception ex) matches any exception: all exception classes derive from Exception. So blocks go from specific to general: concrete classes first, Exception last. Interviewers like to ask this: in what order do you write catch blocks, and why?

The when filter

Sometimes the class alone is not enough to decide who handles an exception. Then you write a when condition after catch. Such a condition is called a filter: the block runs only if the class matches and the condition is true.

C#
string text = "";
try
{
    int n = int.Parse(text);
    Console.WriteLine(n);
}
catch (FormatException) when (text == "")
{
    Console.WriteLine("Empty input"); // Empty input
}
catch (FormatException ex)
{
    Console.WriteLine(ex.Message);
}

An empty string is not a number, so int.Parse throws a FormatException. The condition text == "" is true, and the first block prints Empty input. With text = "x", the condition is false, and the first block is skipped as if it were not there. The second block then catches the exception and prints The input string 'x' was not in a correct format.

Common mistake

The general block was placed above the specific one:

C#
try
{
    Console.WriteLine(100 / int.Parse("0"));
}
catch (Exception ex)
{
    Console.WriteLine("Error: " + ex.Message);
}
catch (DivideByZeroException) // ✗
{
    Console.WriteLine("Zero");
}
TEXT
error CS0160: A previous catch clause already catches all exceptions of this or of a super type ('Exception')

The program does not compile. catch (Exception ex) comes first and catches everything, DivideByZeroException included, so the second block could never run. The compiler notices this in advance: the block above waits for the base class Exception, so it already catches DivideByZeroException too. Swap the blocks: DivideByZeroException above, Exception below.

Key points

  • Code that may throw an exception goes into try, and the handler into catch with the exception class in parentheses. After the handler, the program goes on.
  • After an exception, the rest of try is skipped. The text of the message is in ex.Message.
  • catch blocks are checked from top to bottom, and the first matching one runs; when adds a condition to the class.
  • Concrete classes go above the general Exception, or the compiler reports error CS0160.