Exception filters give developers the ability to add a condition (in the form of a boolean
expression) to a catch block, allowing the catch
to execute only if the condition evaluates to true
.
Exception filters allow the propagation of debug information in the original exception, where as using an if
statement inside a catch
block and re-throwing the exception stops the propagation of debug information in the original exception. With exception filters, the exception continues to propagate upwards in the call stack unless the condition is met. As a result, exception filters make the debugging experience much easier. Instead of stopping on the throw
statement, the debugger will stop on the statement throwing the exception, with the current state and all local variables preserved. Crash dumps are affected in a similar way.
Exception filters have been supported by the CLR since the beginning and they’ve been accessible from VB.NET and F# for over a decade by exposing a part of the CLR’s exception handling model. Only after the release of C# 6.0 has the functionality also been available for C# developers.
Exception filters are utilized by appending a when
clause to the catch
expression. It is possible to use any expression returning a bool
in a when
clause (except await). The declared Exception variable ex
is accessible from within the when
clause:
var SqlErrorToIgnore = 123;
try
{
DoSQLOperations();
}
catch (SqlException ex) when (ex.Number != SqlErrorToIgnore)
{
throw new Exception("An error occurred accessing the database", ex);
}
Multiple catch
blocks with when
clauses may be combined. The first when
clause returning true
will cause the exception to be caught. Its catch
block will be entered, while the other catch
clauses will be ignored (their when
clauses won’t be evaluated). For example:
try
{ ... }
catch (Exception ex) when (someCondition) //If someCondition evaluates to true,
//the rest of the catches are ignored.
{ ... }
catch (NotImplementedException ex) when (someMethod()) //someMethod() will only run if
//someCondition evaluates to false
{ ... }
catch(Exception ex) // If both when clauses evaluate to false
{ ... }
Caution
It can be risky to use exception filters: when an Exception
is thrown from within the when
clause, the Exception
from the when
clause is ignored and is treated as false
. This approach allows developers to write when
clause without taking care of invalid cases.
The following example illustrates such a scenario:
public static void Main()
{
int a = 7;
int b = 0;
try
{
DoSomethingThatMightFail();
}
catch (Exception ex) when (a / b == 0)
{
// This block is never reached because a / b throws an ignored
// DivideByZeroException which is treated as false.
}
catch (Exception ex)
{
// This block is reached since the DivideByZeroException in the
// previous when clause is ignored.
}
}
public static void DoSomethingThatMightFail()
{
// This will always throw an ArgumentNullException.
Type.GetType(null);
}
Note that exception filters avoid the confusing line number problems associated with using throw
when failing code is within the same function. For example in this case the line number is reported as 6 instead of 3:
1. int a = 0, b = 0;
2. try {
3. int c = a / b;
4. }
5. catch (DivideByZeroException) {
6. throw;
7. }
The exception line number is reported as 6 because the error was caught and re-thrown with the throw
statement on line 6.
The same does not happen with exception filters: