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:
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"); // EndLet's go through the program step by step:
Startis printed as usual.- The
tryblock holds, in its braces, the code that may fail. int.Parse("ten")throws aFormatException. The rest of thetryblock is skipped:Console.WriteLine(n)does not run.- The block
catch (FormatException ex)waits for exceptions of the classFormatException. The class matches, and the block prints the message. - After the
catch, the program goes on and printsEnd. 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:
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 aDivideByZeroException. The first block waits forFormatExceptionand does not match; the second printsError: Attempted to divide by zero."ten":int.Parsethrows aFormatException, and the first block printsNot a number."8": there is no exception, the program prints12, 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.
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:
try
{
Console.WriteLine(100 / int.Parse("0"));
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
}
catch (DivideByZeroException) // ✗
{
Console.WriteLine("Zero");
}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 intocatchwith the exception class in parentheses. After the handler, the program goes on. - After an exception, the rest of
tryis skipped. The text of the message is inex.Message. catchblocks are checked from top to bottom, and the first matching one runs;whenadds a condition to the class.- Concrete classes go above the general
Exception, or the compiler reports errorCS0160.