33

C# 6 has a new feature called "exception filtering"

The syntax is like this:

catch (Win32Exception exception) when (exception.NativeErrorCode == 0x00042)  
{  
    //Do something here 
}  

I couldn't help but wonder what the benefit of that is over the current approach:

catch (Win32Exception exception)   
{
     if (exception.NativeErrorCode == 0x00042)
     {
          //Do something here 
     }
}

Is it a big deal that filtering happen before the curly bracket? Perhaps in relation to performance or security?

Diego
  • 19,494
  • 4
  • 32
  • 46
stackunderflow
  • 3,811
  • 5
  • 31
  • 43
  • 3
    What if it is the only one you need to handle? (Note that your sample of "same as" is missing `throw;` for `else` branch, which somewhat shows that you need to write less code with new feature). – Alexei Levenkov Nov 22 '14 at 20:22
  • I'm guessing this feature ties into the underlying Win32 APIs, which support exception filtering. – Jonathon Reinhart Nov 22 '14 at 20:26
  • 3
    This construct has always been available in VB.NET, `Catch When` clause. Giving C# and VB.NET parity has been a strong goal for Microsoft. Which is what this is *really* about. – Hans Passant Nov 22 '14 at 22:36

5 Answers5

35

The Exception Filters feature in C# 6.0 provides various benefits. Here's an explanation of some (ordered by my perceived importance)

  • Feature Parity - Exception filters were already implemented in the IL level and the other .Net languages (VB.Net & F#)[1] and as part of building the new compiler for C# and VB.Net (project "Roslyn") many features existing in one language and lacking in the other were implemented[2].

  • Crash Dumps - Exception filters don't modify the stack. which means that if it gets dumped (in a crash dump) you would be able to know where the exception was originally thrown and not only where it was rethrown (which is irrelevant to the actual problem)[3]

  • Debugging - When an exception enters a catch block, rethrown using throw; and isn't handled anywhere else in the stack (and the exception settings are set to break when the exception is user-unhandled) the debugger would break on throw; instead of where the exception is originally thrown (i.e. in the example below it would break on throw; and not throw new FileNotFoundException();)

  • Multiple catch Blocks - Without exception filters you must catch the exception, check a condition and if it isn't met throw; the exception. The rethrown exception doesn't consider any other catch blocks even if the exception satisfies all the conditions which ultimately results in a single big catch block

    try
    {
        throw new FileNotFoundException();
    }
    catch (FileNotFoundException e)
    {
        if (!e.FileName.Contains("Hamster"))
        {
            throw;
        }
        // Handle
    }
    catch (Exception e)
    {
        // Unreachable code
    }
    
  • Readability - While you could use a "catch all" catch block with many conditions and throw; when they are not met (while suffering the modification to the stack) it's much clearer to have separate and distinct catch blocks where each handles a specific problem in the appropriate way:

    try
    {
    }
    catch (Win32Exception exception) when (exception.NativeErrorCode == 0x00042)  
    { 
    }  
    catch (Win32Exception exception) when (exception.NativeErrorCode == 0x00011)  
    {  
    }
    catch (IOException)
    {  
    }
    
  • "Abuse" - You could use exception filters as a way to inspect an exception without handling it. While this is not a main benefit it's a nice side effect. You can have a false-returning logging method and an empty catch block:

    private static bool LogException(Exception exception)
    {
        Console.WriteLine(exception.Message);
        return false;
    }
    
    try
    {
    }
    catch (ArgumentException exception) when (LogException(exception))  
    {
        // Unreachable code.
    }
    

In conclusion, most of C# 6.0 features are small improvements and syntactic sugar, and while exception filters isn't a very big feature it does provide functionality that wasn't possible before.


  1. A C# 6.0 Language Preview

    The other exception improvement in C# 6.0—support for exception filters—brings the language up-to-date with other .NET languages, namely Visual Basic .NET and F#

  2. Languages features in C# 6 and VB 14

  3. New Features in C# 6

    Exception filters are preferable to catching and rethrowing because they leave the stack unharmed. If the exception later causes the stack to be dumped, you can see where it originally came from, rather than just the last place it was rethrown.

i3arnon
  • 113,022
  • 33
  • 324
  • 344
  • 1
    @Jeroen the same reason you use the new operator `?.` instead of a null check + Boolean expression. It's shorter and more concise. – Scott Chamberlain Nov 22 '14 at 20:37
  • @Scott: Exception filters are NOT just syntactic sugar – Ben Voigt Nov 22 '14 at 20:39
  • 1
    @BenVoigt I never said they were, I did not say it acted the same way as `?.`, just it was useful for the same reasons. – Scott Chamberlain Nov 22 '14 at 20:42
  • @ScottChamberlain: It is shorter and more concise, but contrary to your comment, that's not the reason it is useful. – Ben Voigt Nov 22 '14 at 20:44
  • A variation of what you call "abuse" is using a method to make an exception available in the `finally` clause (which IMHO it always should have been). If a `finally` clause which is run after a successful `try` block throws an exception, that exception should be allowed to propagate out, but if the `try` block exited with an exception the `finally` block should not obscure the inner exception. The only way to meet both requirements is to have the `finally` block know whether an exception occurred within the `try`--something which can be done--albeit kludgily--with filters. – supercat Jun 09 '15 at 21:48
12

Eren Ersönmez already mentioned the most important benefit.

I just want to add that when an exception is caught, it has the effect of unwinding the stack. That means that if you rethrow the exception with throw; and it isn't caught higher up the stack, the debugger will highlight the throw, not the place where the exception was initially thrown, so you can't see the state of the variables when the exception was thrown.

On the other hand, exception filters do not unwind the stack, so if the exception isn't caught, the debugger will show the place where the exception was initially thrown.

So if you have this:

try
{
    DoSomethingThatThrows();
}
catch (Win32Exception exception)   
{
     if (exception.NativeErrorCode == 0x00042)
     {
          //Do something here 
     }
     else
     {
         throw;
     }
}

the debugger will stop on throw when NativeErrorCode is not 0x42.

But if you have this:

try
{
    DoSomethingThatThrows();
}
catch (Win32Exception exception) if (exception.NativeErrorCode == 0x00042)
{         
     //Do something here 
}

the debugger will stop in DoSomethingThatThrows (or in a method called by it), letting you see more easily what caused the error (again, when NativeErrorCode is not 0x42)

Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • This is the answer. Exception filters are executed at a different time with respect to stack unwinding and debugging. This looks like a good explanation: http://dralu.com/?p=184 – Ben Voigt Nov 22 '14 at 20:42
  • 1
    That's only true when the exception settings are set to break when the exception is user-unhandled and not when it's thrown. – i3arnon Nov 22 '14 at 20:55
7

From the official C# feature descriptions (PDF download link, as at VS2015 Preview):

Exception filters are preferable to catching and rethrowing because they leave the stack unharmed. If the exception later causes the stack to be dumped, you can see where it originally came from, rather than just the last place it was rethrown.

It is also a common and accepted form of “abuse” to use exception filters for side effects; e.g. logging. They can inspect an exception “flying by” without intercepting its course. In those cases, the filter will often be a call to a false-returning helper function which executes the side effects:

private static bool Log(Exception e) { /* log it */ ; return false; }
…
try { … } catch (Exception e) if (Log(e)) {}
svick
  • 236,525
  • 50
  • 385
  • 514
Gigi
  • 28,163
  • 29
  • 106
  • 188
6

It allows checking for a condition without catching the exception, which means if the condition is not met, the next catch block is considered. If you catch and re-throw, the next catch block won't be considered.

Eren Ersönmez
  • 38,383
  • 7
  • 71
  • 92
5

The real benefit IMO:

try
{
}
catch (SomeException ex) if (ex.Something)
{
}
catch (Exception ex)
{
  // SomeException with !ex.Something is caught here
  // not the same as calling `throw` for !ex.Something 
  // without filters in previous handler
  // less code duplication
}
leppie
  • 115,091
  • 17
  • 196
  • 297