Exception filters

This is one of a series of posts on some of the new features in C# 6.0, which was just released.

When catching exceptions in code, you can have a different catch block for different types of exceptions:

try
{
}
catch(SqlException ex)
{
  // Do something
}
catch(Exception ex)
{
  // Log error
}

But if you want to handle exceptions differently depending on the details of the exception, you could not until now — you would have to build the logic into the block:

try
{
}
catch(SqlException ex)
{
  if(ex.Number == 2627)
  {
    // Handle primary key violation
  }
  else
  {
    // Do exactly the same thing as the main exception block
  }
}
catch(Exception ex)
{
  // Log error
}

Now, though, you can add conditions to your catch block:

try
{
}
catch(SqlException ex) when (ex.Number == 2627)
{
    // Handle primary key violation
}
catch(Exception ex)
{
  // Log error
}

Now, the first catch block will only get called if the specific error is handled. You could equally have multiple catches for the same type of exception, but with different details:

try
{
}
catch(SqlException ex) when (ex.Number == 2627)
{
    // Handle primary key violation
}
catch(SqlException ex) when (ex.Number == 2601)
{
    // Handle unique index exception
}
catch(SqlException ex)
{
   // Handle other SQL Exception
}
catch(Exception ex)
{
  // Log error
}

This not only makes code cleaner in general, it also avoids duplicate code in different blocks (duplicate code is very high up in my list of coding sins).

Note that you can also have more complex logic in the condition, and can call methods:

catch(SqlException ex) when (ex.Number == 2627 || ex.Number == 2601)

or

catch(SqlException ex) when (IsAConstraintViolation(ex.Number))

Which leads to an interesting semi-evil trick – since you can call a method, that method can do something with the exception, then return false, meaning that you are responding to the exception, but not catching it. For example, you could log the specific exception, but then let handling fall through to the next exception handler:

try
{
}
catch(SqlException ex) when (LogSqlException(ex))
{
}
catch(Exception ex)
{
  // Standard exception handling here
}

Of course, LogSqlException() would have to return false. I think that logging-type operations are the only acceptable things to handle in this way since this would otherwise generate a maintenance nightmare. I suspect, though, that we will see some interesting uses of this…

 

Here are links to posts about other new features in C# 6.0:

Leave a Reply

Your email address will not be published. Required fields are marked *

*