try, catch and finally
When something goes wrong that the code at that point cannot deal with (a file is missing, a number does not parse, you divide by zero), .NET throws an exception: an object describing the problem. Normal execution stops, and the runtime walks back up the call stack looking for a catch block that accepts that kind of exception. If none is found, the program ends with an "Unhandled exception" message.
A try block marks code whose exceptions you want to handle. A catch block handles them. A finally block runs whether or not an exception happened, even if the try returned early, which makes it the place for clean-up.
Console.WriteLine(Divide(10, 2));
Console.WriteLine(Divide(10, 0));
static string Divide(int a, int b)
{
try
{
Console.WriteLine($"trying {a} / {b}");
return $"= {a / b}";
}
catch (DivideByZeroException ex)
{
return $"failed: {ex.Message}";
}
finally
{
Console.WriteLine("finally runs either way");
}
}trying 10 / 2
finally runs either way
= 5
trying 10 / 0
finally runs either way
failed: Attempted to divide by zero.The finally line prints before the returned value, because finally runs on the way out of the method, before the caller gets the result.
try{Console.WriteLine("start");int zero = 0;int x = 10 / zero;Console.WriteLine("never printed");}catch (DivideByZeroException){Console.WriteLine("caught");}finally{Console.WriteLine("clean up");}Console.WriteLine("carry on");
The try block starts running normally.
nothing yet
startAll 7 steps as a table
| Step | Line | What happened | Variables now |
|---|---|---|---|
| 1 | 3 | The try block starts running normally. | |
| 2 | 4 | A local set to 0. | zero = 0 |
| 3 | 5 | Integer division by zero: the runtime throws a DivideByZeroException. The rest of the try block is skipped. | exception = DivideByZeroException |
| 4 | 8 | The runtime checks the catch clauses in order. This one matches the exception's type. | |
| 5 | 10 | The handler runs. The exception is now handled. | exception = (handled) |
| 6 | 14 | finally always runs after try/catch. | |
| 7 | 16 | Execution continues after the whole statement, as if nothing had happened. |
CS0165: using a variable assigned only inside try
string input = "42x";
int n;
try
{
n = int.Parse(input);
}
catch (FormatException)
{
Console.WriteLine("not a number");
}
Console.WriteLine(n + 1);Program.cs(11,19): error CS0165: Use of unassigned local variable 'n'If int.Parse throws, the assignment never happens, the catch block runs, and execution reaches the last line with n still unassigned. The compiler follows that path and refuses to let you read a variable that might have no value.
Give the variable a value on every path (a default before the try, or in the catch). Better, for parsing, use int.TryParse and skip the exception entirely (last lesson).
string input = "42x";
int n = 0;
try
{
n = int.Parse(input);
}
catch (FormatException)
{
Console.WriteLine("not a number");
}
Console.WriteLine(n + 1);