Skip to content
wannacode

Different errors and finally

Problem

The bill for zero people

The program splits the restaurant bill evenly. You already know about wrong input, and you catch it:

Program.cs
class Program
{
static void Main()
{
try
{
int bill = int.Parse(Console.ReadLine());
int people = int.Parse(Console.ReadLine());
Console.WriteLine("Per person: €" + bill / people);
}
catch (FormatException)
{
Console.WriteLine("Enter whole numbers.");
}
}
}

The bill is €90, and someone enters 0 for the number of people. The program stops again:

Error
Unhandled exception. System.DivideByZeroException: Attempted to divide by zero.
at Program.Main() in /projects/Bill/Program.cs:line 9

catch is there, but it does not help. It catches only FormatException, and this is a different kind of exception.

The idea

Several catch blocks

One try can be followed by several catch blocks, one for each kind of error:

try
{
int bill = int.Parse(Console.ReadLine());
int people = int.Parse(Console.ReadLine());
Console.WriteLine("Per person: €" + bill / people);
}
catch (FormatException)
{
Console.WriteLine("Enter whole numbers.");
}
catch (DivideByZeroException)
{
Console.WriteLine("People cannot be 0.");
}
Input Output
90 and 3 Per person: €30
90 and 0 People cannot be 0.
90 and three Enter whole numbers.

The blocks are checked from top to bottom. The first one whose kind matches runs, and only that one. The rest are skipped.

try
{
int bill = int.Parse(Console.ReadLine());
int people = int.Parse(Console.ReadLine());
Console.WriteLine("Per person: €" + bill / people);
}
catch (FormatException)
{
Console.WriteLine("Enter whole numbers.");
}
catch (DivideByZeroException)
{
Console.WriteLine("People cannot be 0.");
}
Step by step, in words
  1. First run: the bill is €90, and there are 0 people.
  2. 90 / 0 is division by zero: DivideByZeroException is thrown.
  3. The first catch is for FormatException. It does not match, so it is skipped.
  4. The second one matches. It runs, and only it.
  5. Second run: "three" is typed in instead of the number of people.
  6. int.Parse cannot make a number: FormatException is thrown.
  7. The very first catch matches. The second is never reached.
  8. The blocks are checked from top to bottom. The first one that matches runs.
More precisely, if you are curious
  • catch can also have a condition: catch (FormatException) when (text == ""). The block catches only if the condition is true.

The idea

The family of exceptions

Exceptions are classes, related by inheritance. Here are the most common ones:

The hierarchy
Exception
├── SystemException
│ ├── ArithmeticException
│ │ ├── DivideByZeroException
│ │ └── OverflowException
│ ├── FormatException
│ ├── IndexOutOfRangeException
│ ├── NullReferenceException
│ ├── ArgumentException
│ │ ├── ArgumentNullException
│ │ └── ArgumentOutOfRangeException
│ └── IOException files, network
└── ApplicationException

catch catches the kind you name and all of its subclasses. This is the polymorphism from the module on inheritance: a variable of a parent type can point to an object of a subclass. That is why catch (Exception e) catches everything:

static void Split(string billText, string peopleText)
{
try
{
int bill = int.Parse(billText);
int people = int.Parse(peopleText);
Console.WriteLine("Per person: €" + bill / people);
}
catch (Exception e)
{
Console.WriteLine(e.GetType().Name + ": " + e.Message);
}
}
Split("90", "3");
Split("90", "0");
Split("90", "three");
Output
Per person: €30
DivideByZeroException: Attempted to divide by zero.
FormatException: The input string 'three' was not in a correct format.

e.GetType().Name gives the name of the object's real class. The variable is of type Exception, but the object is of a subclass.

More precisely, if you are curious
  • StackOverflowException cannot be caught at all: .NET stops the program right away. OutOfMemoryException can be, but it rarely makes sense.
  • ApplicationException was meant to be the parent of the exceptions programs define. In practice, you make your own exceptions straight from Exception.
  • Catch as specifically as you can. catch (Exception e) also hides errors you did not expect, such as a bug in your own code.

Try it

Which catch?

What will the program print?

int[] seats = { 4, 2, 6 };
try
{
Console.WriteLine("Table 4: " + seats[3] + " seats");
}
catch (ArithmeticException)
{
Console.WriteLine("Division by zero");
}
catch (SystemException)
{
Console.WriteLine("Runtime error");
}
catch (Exception)
{
Console.WriteLine("General error");
}
Answer

Try it

Find the bug

This program does not compile. Which line is the bug on?

Pick the line with the bug.

The idea

The compiler does not make you

Java has checked exceptions: the compiler does not accept the program until you catch them or declare them. C# has none of those. No exception has to be caught:

static void PrintReceipt()
{
Console.WriteLine("Printing the receipt...");
Thread.Sleep(300);
Console.WriteLine("Done.");
}
PrintReceipt();
Output
Printing the receipt...
Done.

Thread.Sleep pauses the program for 300 milliseconds and can throw an exception, but the compiler asks for nothing. The same goes for int.Parse.

So the responsibility is yours. Which exceptions a method throws is written in its documentation, in the "Exceptions" section. Read it before you decide whether to catch.

The idea

finally: always

The till's printer has to be freed after every receipt, or the next one will not print. Whatever happens, success or error:

static void PrintReceipt(string qtyText)
{
Console.WriteLine("The printer is busy.");
try
{
int qty = int.Parse(qtyText);
Console.WriteLine("Coffee: " + qty + " x €2 = €" + qty * 2);
}
catch (FormatException)
{
Console.WriteLine("Wrong quantity: " + qtyText);
}
finally
{
Console.WriteLine("The printer is free.");
}
}
PrintReceipt("3");
PrintReceipt("three");
Output
The printer is busy.
Coffee: 3 x €2 = €6
The printer is free.
The printer is busy.
Wrong quantity: three
The printer is free.

finally runs always: when everything goes well, when the exception is caught, and even when none of these catch blocks catches it. In that last case finally runs, and the exception keeps going up to the next try that can catch it.

Why not just a line after the try/catch? Because that line is skipped if the exception is of a kind that catch does not catch, or if try has a return. finally is not skipped.

More precisely, if you are curious
  • You can also have try with finally and no catch: the exception is not caught, but the cleanup runs.
  • For resources that have to be closed, such as files and database connections, there is a shorter form: using. You will meet it when you work with files and databases. It closes the resource by itself, just as finally would.
  • finally does not run only if the whole program is stopped, for example with Environment.Exit(0).
  • If the exception is not caught anywhere in the program, .NET may stop it without running the finally blocks. So catch exceptions at least in Main if there is something important in finally.

Try it

finally and return

What will the program print?

static string Reserve(int guests)
{
try
{
if (guests > 6)
{
return "No table for " + guests;
}
return "Table for " + guests;
}
finally
{
Console.WriteLine("Logged the attempt.");
}
}
Console.WriteLine(Reserve(8));
Answer

Exercise

Programs with several catch blocks

Solve these in your editor. Do not print prompts.

1. Price per person. The prices are in the array { 120, 75, 300 }. Read a product number (from 1) and the number of people, and print the price per person with integer division. Catch three things: a product that does not exist, zero people, and everything else (then print the exception's message).

Input Output
2 and 3 Per person: €25
5 and 2 No such product.
1 and 0 The number of people cannot be 0.
1 and two Error: The input string 'two' was not in a correct format.

2. Calculator. Read a whole number, an operator (+, -, * or /) and a second whole number, each on its own line. Print the calculation. On text instead of a number say "Enter whole numbers.", on division by zero "Division by zero.", and on any other operator "Unknown operator: " followed by the operator.

Input
84
/
4
Output
84 / 4 = 21
Input
84
/
0
Output
Division by zero.

More tasks

  • The card terminal. The method Pay(string amountText) prints "The terminal is connected.", then "Paid €X" or a message about wrong input, and in finally "The terminal is off." Call it with "25" and with "twenty".
  • Average grade without crashes. Read 5 lines with grades. Skip the ones that are not whole numbers (an exception) or are not from 2 to 6 (an if). Print the average with integer division. If no grade is valid, the division by zero throws: catch it and print "No valid grades."
  • Predict and check. Write a try with catch and finally, where each block prints a line. Predict the output on success, on a caught exception and on an uncaught one, then run it.

Test

Check yourself

1. The exception matches two of the three catch blocks. Which one runs?
2. Does catch (SystemException e) catch an exception of type FormatException?
3. Why does catch (Exception e) have to come last?
4. When does finally run?
5. Does the C# compiler make you catch FormatException around int.Parse?

Made by Ivo Gergov as part of Evo Tech's campaign for accessible programming education ·