Exception Handling
Exception handling is a critical part of understanding how errors are handled in C#.
As John Carmack said:
"A large fraction of the flaws in software development are due to programmers not fully understanding all the possible states their code may execute in."
The golden rule of exception handling is: EXPECT THEM! Understand how they alter control flow and the behavior of your applications. Most built-in .NET methods will show you in the documentation what kinds of exceptions they throw.
Try/Catch
When you "throw" an exception or an exception is "thrown", that means the code has raised an error from which it can't recover. We'll see them mainly from invalid arguments to methods.
Handling an exception means catching it within a try/catch
block as below, and doing something with it (rethrowing, recovering, logging, etc).
The try
block is used to enclose the code that may throw an exception.
The catch
block is used to handle the exception.
Note that code that can throw an exception does not have to be in a try/catch
block.
Exceptions Types
All exception types are of type Exception
. We can see that by digging into the source code for any given exception.
In order to be thrown, an exception must be of type Exception
.
Example:
try
{
// Code that may throw an exception
}
catch (Exception ex)
{
// Code to handle the exception
}
Finally
A block that executes code after try
and catch
, regardless of whether an exception was thrown or not.
Example:
try
{
// Code that may throw an exception
}
catch (Exception ex)
{
// Code to handle the exception
}
finally
{
// Code that always executes
}
Catching Multiple Types of Exceptions
The catch
block can catch multiple types of exceptions.
try
{
// Code that may throw an exception
}
catch (IOException ex)
{
// Handle IO exceptions
}
catch (ArgumentException ex)
{
// Handle argument exceptions
}
Exception Filtering
Filtering exceptions based on specific conditions.
Example:
try
{
// Code that may throw an exception
}
catch (Exception ex) when (ex.Message.Contains("specific condition"))
{
// Handle exceptions that meet the condition
}
Throwing your own exceptions
You can throw your own exceptions using the throw
keyword.
Let's say you have a method that expects a string of a certain kind, and it throws an exception if it doesn't get what it wants. Commonly, you'll throw an ArgumentException
if you have a method that takes a parameter, and the caller passes in an invalid value for that parameter.
public void DoSomething(string input)
{
if (string.IsNullOrWhiteSpace(input))
{
throw new ArgumentException("Input cannot be null or empty", nameof(input));
}
}
Creating your own exception types
We'll cover object inheritance more later and at that time, this will make more sense. But for now, just know that you can create your own exception types by creating a new class that inherits from Exception
.
public class MyCustomException : Exception
{
public MyCustomException(string message) : base(message)
{
}
}
Spencer doesn't do it often - oftentimes I just throw InvalidOperationException
or ArgumentException
. I will throw custom exceptions in company-specific framework code where it's merited.