- Home
- .NET tutorials
- When does the try, catch and finally code blocks run in C#?
When does the try, catch and finally code blocks run in C#?
Published: Monday 17 June 2024
The try
, catch
and finally
statements are excellent for catching unhandled exceptions. They are also good for ensuring that a code block always runs regardless of whether an exception is thrown or not.
But do they always run? Let's investigate!
The purpose of the try block
The purpose of the try
block is to write code where there is a risk of an exception being thrown.
C# coding challenges
There are many scenarios why you would want to do this. One example is communicating with a service outside of the application, such as a database or an API. These are prone to unexpected issues, such as connectivity or resource management issues.
The catch
statement ensures that these exceptions can be handled without the application crashing.
In this example, we are making a call to an API and logging the exception to the console if one occurs.
var httpClient = new HttpClient();
try
{
var response = await httpClient.GetAsync("https://www.roundthecode.com/api/products/1");
response.EnsureSuccessStatusCode();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Exception classes
In the code snippet above, we were catching the exception if it uses the Exception
class. As all exceptions inherit this class, it will always catch any exception thrown.
But C# has a number of other common exceptions that you can use. They include:
ArgumentException
- One of the arguments provided to a method is not validDivideByZeroException
- Attempting to divide a number by zeroIndexOutOfRangeException
- Attempting to get the value of an array index that is outside the bounds of the arrayNullReferenceException
- Using a null reference object in a way that requires an instance, such as calling a property or a methodOutOfMemoryException
- The application can not allocated enough memory
Any of these exceptions can be handled in a separate catch
code block.
In this example, we are passing in an object as a parameter into the method and catching the exception if a NullReferenceException
is thrown:
public class CategoryService
{
public string? GetCategoryName(Category category)
{
try
{
return category.Name;
}
catch (NullReferenceException)
{
return null;
}
}
}
However, if any other exceptions are thrown, they would be unhandled.
But you can add multiple catch
code blocks to a try
statement. In this example, if the exception thrown is a NullReferenceException
, the method will return null
. If any other exceptions are thrown, the exception is written to the console and then the method returns null
.
public class CategoryService
{
public string? GetCategoryName(Category category)
{
try
{
return category.Name;
}
catch (NullReferenceException)
{
// Code block will only be run if a NullReferenceException is thrown in the 'try' code block
return null;
}
catch (Exception ex)
{
// Code block will run if anything except a NullReferenceException is thrown in the 'try' code block
Console.WriteLine(ex.Message);
return null;
}
}
}
How to write your own Exception class
There may be instances when you want to write your own exception class. This can be done by creating a class and inheriting the Exception
class.
One of the benefits of creating your own is that you can add your own members that are specific to the exception.
However, you'll need to override some of the base constructors to add an error message and an inner exception.
In this example, we've created a CategoryException
class. We've included three of the base classes with constructors that:
- Are parameterless
- Has a message as a string as a parameter
- Has a message as a string and an inner exception as parameters
In-addition, we are passing in the category name and it's being saved as a read-only property as part of the exception.
public class CategoryException : Exception
{
public string CategoryName { get; }
public CategoryException(string categoryName)
: base()
{
// Does not add an error message
CategoryName = categoryName;
}
public CategoryException(string categoryName, string message)
: base(message)
{
// Add an error message through the base 'Exception' class
CategoryName = categoryName;
}
public CategoryException(string categoryName, string message, Exception? innerException)
: base(message, innerException)
{
// Add an error message and inner exception through the base 'Exception' class
CategoryName = categoryName;
}
}
If we wish to catch this exception, we can add it to our try
, catch
statement.
public class CategoryService
{
public string? GetCategoryName(Category category)
{
try
{
return category.Name;
}
catch (CategoryException)
{
return null;
}
catch (NullReferenceException)
{
return null;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return null;
}
}
}
When do the code blocks run?
Now that we've established how the try
, catch
statement works, that's examine when they run and how the finally
statement works.
No exception in try block
var load = true;
try
{
var a = 9 + 9;
}
catch (Exception ex)
{
// Does not run as there is no exception
}
finally
{
// Runs after the try code block
load = false;
}
There's a try
code block that doesn't throw an exception. We are catching any exception, but not doing anything with it.
in this situation, the try
block will execute, but as no exception is thrown, it will ignore the catch
block and go straight to the finally
block.
Exception is thrown in the try block
var load = true;
try
{
var a = int.Parse("not a number"); // FormatException thrown
var b = 9 + 9; // This does not run as an exception has already been thrown
}
catch (Exception ex)
{
Console.WriteLine(ex.Message); // Will write the FormatException to the Console
}
finally
{
// Runs after the catch code block
load = false;
}
In this situation, the try
block will run up until the point there is an exception.
As "not a number"
is not a valid integer, it will throw a FormatException
. As the exception is thrown before the b
variable is declared, that code snippet will not run.
The finally
block will run after the catch
block has been executed.
Exception is thrown but not the one being catched
var load = true;
try
{
var a = int.Parse("not a number"); // FormatException thrown
var b = 9 + 9; // This does not run as an exception has already been thrown
}
catch (NullReferenceException ex)
{
// This code block will not run as it's a FormatException being thrown
Console.WriteLine(ex.Message);
}
finally
{
// Runs after the try code block in an ASP.NET Core app, but not a Console application
load = false;
}
The try
block is throwing the same FormatException
.
However, we are only catching any exceptions that are a NullReferenceException
type. So the catch
block won't run and the application will throw an exception instead.
Before it does that, it will execute the finally
block if it's an ASP.NET Core app. If it's a console application, it stops before it has a chance to run the finally
block.
Exception is thrown in the catch block
var load = true;
try
{
var a = int.Parse("not a number"); // FormatException thrown
var b = 9 + 9; // This does not run as an exception has already been thrown
}
catch (Exception ex)
{
// Throws the exception causing the application to throw an exception
throw ex;
}
finally
{
// Runs after the catch code block in an ASP.NET Core, but not in a Console application
load = false;
}
When the try
block throws the FormatException
, it will execute the catch
block.
But because the exception in the catch
block is also being thrown, the application will throw an exception.
The finally
block will run before the application throws an exception in an ASP.NET Core app. But in a Console application, it will not run as the application would have already stopped.
No catch block
var load = true;
try
{
var a = int.Parse("not a number"); // FormatException thrown
var b = 9 + 9; // This does not run as an exception has already been thrown
}
finally
{
// Runs after the try code block in an ASP.NET Core application, but not a Console application
load = false;
}
We have no catch
statement and the FormatException
is being thrown in the try
statement.
Like with the other examples, the application will throw an exception as it's being unhandled.
But even without the catch
block, the finally
block will still run in an ASP.NET Core app. But like with the other scenarios, the Console application will stop before it can run the finally
block.
Exception thrown in the finally block
var load = true;
try
{
var a = int.Parse("not a number"); // FormatException thrown
var b = 9 + 9; // This does not run as an exception has already been thrown
}
catch (Exception ex)
{
Console.WriteLine(ex.Message); // Will write the FormatException message to the Console
}
finally
{
var c = int.Parse("not a number"); // FormatException thrown
load = false; // This does not run as an exception has already been thrown
}
The FormatException
gets thrown in the try
block.
The exception is captured and logged to the console.
The finally
block will still run. However, a FormatException
is thrown when we try to pass the integer with a non-numeric string.
Therefore, an exception will be thrown and the load
boolean will not be changed to false
.
Watch the video
Watch our video where we go through the try
, catch
and finally
code blocks, what exceptions are available, how to write your own custom exception and when the code blocks run.
Catch runs if there is an exception. Finally always runs
In conclusion, the catch
statement will only run if an exception is thrown in the try
block, and it's the same exception declared in the catch
statement.
In ASP.NET Core, the finally
statement will always run, regardless of whether an exception is thrown in a try
or catch
block, or whether that exception is handled or not.
But with a Console application, the finally
statement doesn't run if an unhandled exception occurs.
When an unhandled exception is thrown, it will not execute any code in that block that proceeds the thrown exception. This applies to any of the code blocks.