Exception filters is a new C# 6.0 feature. Visual Basic.NET and F# have this functionality for a long time. That is because exception filtering was implemented in CIL but not in C#. Now, this technique available for us. That's how you can use it:

try { Method ( ) ; } catch ( Win32Exception ex ) when ( ex . NativeErrorCode == 0x07 ) { } catch ( Win32Exception ex ) when ( ex . NativeErrorCode == 0x148 ) { } catch ( Exception ) { throw ; }

If Method throws an exception, catch block will first try to match type of the exception and then will evaluate when block. If an expression in when block is true, execution will go to the catch block. If not, the next catch block will be evaluated.

Internal Implementation

Let's see simple example:

try { } catch ( Exception ex ) when ( ex . Message == "test" ) { throw ; }

and IL code:

. try { IL_0000 : leave . s IL_0025 } filter { IL_0002 : isinst [ mscorlib ] System . Exception IL_0007 : dup IL_0008 : brtrue . s IL_000e IL_000a : pop IL_000b : ldc . i4 . 0 IL_000c : br . s IL_0020 IL_000e : callvirt instance string [ mscorlib ] System . Exception :: get_Message ( ) IL_0013 : ldstr "test" IL_0018 : call bool [ mscorlib ] System . String :: op_Equality ( string , string ) IL_001d : ldc . i4 . 0 IL_001e : cgt . un IL_0020 : endfilter } { IL_0022 : pop IL_0023 : rethrow }

as you can see, instead of a catch block, we have a filter block. First, there is a check if exception is of the specified type (IL_0002: isinst [mscorlib]System.Exception) and then we see check from the when block:

IL_000e : callvirt instance string [ mscorlib ] System . Exception :: get_Message ( ) IL_0013 : ldstr "test" IL_0018 : call bool [ mscorlib ] System . String :: op_Equality ( string , string )

and then handler block:

{ IL_0022 : pop IL_0023 : rethrow }

Exceptions In when Block

One question that you might have, what will happen if we get an exception in a when block?

Answer: whole catch block will be ignored and we will move to the next catch block (if defined).

Let's check out simple example:

static void Main ( string [ ] args ) { try { Method ( ) ; } catch ( Exception ex ) { Console . WriteLine ( $ "Catch Exception at root level: {ex.GetType().Name}" ) ; } } public static void Method ( ) { try { Console . WriteLine ( "Throw OutOfMemoryException" ) ; throw new OutOfMemoryException ( ) ; } catch ( OutOfMemoryException ) when ( CheckFirstCondition ( ) ) { Console . WriteLine ( "Catch first OutOfMemoryException" ) ; Console . WriteLine ( "Throw ArgumentOutOfRangeException" ) ; throw new ArgumentOutOfRangeException ( ) ; } catch ( OutOfMemoryException ) when ( CheckSecondCondition ( ) ) { Console . WriteLine ( "Catch second OutOfMemoryException" ) ; throw ; } catch ( Exception ex ) { Console . WriteLine ( $ "Catch Exception in method. Type: {ex.GetType().Name}" ) ; Console . WriteLine ( "Rethrow last exception" ) ; throw ; } } private static bool CheckSecondCondition ( ) { Console . WriteLine ( "Check second condition" ) ; return false ; } private static bool CheckFirstCondition ( ) { Console . WriteLine ( "Check first condition" ) ; Console . WriteLine ( "Throw InvalidOperationException" ) ; throw new InvalidOperationException ( ) ; return true ; }

In console you will see following:

Throw OutOfMemoryException Check first condition Throw InvalidOperationException Check second condition Catch Exception in method . Type : OutOfMemoryException Rethrow last exception Catch Exception at root level : OutOfMemoryException

As we can see, InvalidOperationException has been ignored.

Video version

Guys from Webucator created nice video based on this article (check out their C# course):

https://www.youtube.com/watch?v=BDy83_6rLeU