C# 6.0Exception 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)
{
    // do exception handling logic
}
catch (Win32Exception ex) when (ex.NativeErrorCode == 0x148)
{
    // do exception handling logic
}
catch (Exception)
{
    // log unhandled exception and rethrow
    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

}  // end .try
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
}  // end filter
{  // handler
  IL_0022:  pop
  IL_0023:  rethrow
}  // end handler

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:

{  // handler
  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