Search
Close this search box.

C#/.NET Little Wonders: Exception Filtering in C# 6

Once again, in this series of posts I look at the parts of the .NET Framework that may seem trivial, but can help improve your code by making it easier to write and maintain. The index of all my past little wonders posts can be found here.

Visual Studio 2015 is on the horizon!  In fact, some of you may already have played with the preview and seen some of the many neat new things to come – both in the IDE and in the C# language.

For those who haven’t been keeping up with the announcements, I’m taking some blog time for a few Little Wonders posts to talk about some of the neat new things that will be in C# 6. 

Exception Filtering

C# (like its ancestors Java and C++ before it) has long been able to filter exceptions by type.  For example, if you wanted to be able to perform a different action based on the type of exception caught, you could simply have separate catch blocks to handle those:

try {
  // some code to check  ...
} catch (InvalidOperationException ex) {
  // do your handling for invalid operation ...
} catch (IOException ex) {
  // do your handling for IO error ...
}

However, there comes a time when we’re writing code that we need to make a decision on whether or not to handle an exception based on not only the type of exception, but also on whether a given condition is satisfied.

For example, let’s say you are writing code to perform some operation against a dependency, and that dependency will throw an exception with a bool property that tells you whether the operation IsRetryable

Such an exception may look something like this:

public class SomeDependencyException : Exception {
  public bool IsRetryable { get; private set; }

  public SomeDependencyException(string message, Exception cause,
                                 bool isRetryable = false)
      : base(message, cause) {
    IsRetryable = isRetryable;
  }
}

Now, let’s say that as we’re calling this dependency, we want to be able to retry any error that has IsRetryable == true up to three times, then abandon if it throws a fourth time.  However, if the exception has IsRetryable == false, we do not want to catch the exception at all, but rather let it bubble up to the caller.

Before C# 6, we’d probably write something like this:

while (tries++ < MaxAllowedTries) {
  try {
    dependency.DoSomethingBig();
  } catch (SomeDependencyException ex) {
    if (!ex.IsRetryable) {
      throw;
    }

    Console.WriteLine($"Dependency failure on try {tries}.");
  }
}

Note:The $”…{…}…” syntax is C# 6’s string interpolation feature, for more information click here for my recent post.

Taking a look at this code, a few things stick out.  First of all, our catch block now has extra conditional logic in it, which makes the flow of the program a bit less intuitive and harder to read.  It would be much clearer if we could just tell our catch block we’re only interested in exceptions that meet a certain criteria.

Second, we end up catching and re-throwing an exception we never wanted to catch in the first place.  This is not terribly efficient, it would be much better if we could avoid the cost of the catch & re-throw rather than just not catch the original throw.

Exception Filtering in C# 6

Filtering exceptions by type is still, of course, available in C# 6 and 99% of the time this will get you what you need in your exception handling.  C# 6, however, adds the ability to do that very thing we wanted in our last example: only catch an exception if a certain condition is met.

The magic is in the when keyword.  This keyword is specified as part of the catch statement and lists the condition that must be satisfied for the exception to be caught by that block (provided, of course, that it meets the type filter as well).

catch (SomeException ex) when (someConditionIsMet)
{
    // Your handler logic
}

This means that if the exception throw was of type SomeException and someConditionIsMet is true, then the exception will be caught and handled in that catch block.  If not, the exception will not be caught by that catch

Note: In previous versions of the Visual Studio 2015 CTP, the if keyword was used instead of when.

Note that it does not catch-and-rethrow behind the scenes, it is simply not caught by that catch block at all which means that you won’t incur the cost of catching and re-throwing the exception if it is not one that you care about.

So how would our example look using this feature?

while (tries++ < MaxAllowedTries)
{
    try
    {
        dependency.DoSomethingBig();
    }
    catch (SomeDependencyException ex) when (ex.IsRetryable)
    {
        Console.WriteLine($"Dependency failure on try {tries}.");
    }
}

Notice how much more clear and concise this now is.  We are now stating directly in the catch…when clause what we are catching and when.  If the type or condition do not match, the exception is not caught and will pass through.

Of course, this loop would end up absorbing this exception after the max pass.  That may not be our intention, what if instead we want to try up to three times and if, on the third try, a retryable exception was still thrown we want to let it bubble out?

We can easily do this since the condition in the catch…when clause does not necessarily have to involve the reference to the exception at all.  That is, we could examine if we have any tries left and determine whether to catch or not like this:

catch (SomeDependencyException ex) when (ex.IsRetryable && tries < MaxAllowedTries)
{
    Console.WriteLine($"Dependency failure on try {tries}.");
}

So you may be wondering now, if you can have a condition with a catch…when clause, can I have more than one and have two different catch blocks for when two different conditions are true?  Absolutely!

You can have multiple catch…when filters for a given type, or a catch…when followed by a plain catch for an exception type:

catch (SomeDependencyException ex) when (ex.IsRetryable && tries < MaxAllowedTries)
{
    Console.WriteLine($"Dependency failure on try {tries}, will retry.");
}
catch (SomeDependencyException ex) when (ex.IsRetryable)
{
    Console.WriteLine($"Dependency failure aborted after {tries} tries.");
}
catch (SomeDependencyException ex) 
{
    Console.WriteLine("Dependency failure was non-retryable, aborting.");
}

In this case, the exception filters will be checked in order to see which catch will get the SomeDependencyException.  So if we get a retryable exception on the first few throws, the first catch…when will get the exception.  If, however, we get to the point where tries == MaxAllowedTries and we’re still retryable, the second catch…when will catch us.  Or, if we ever get an exception that is not retryable, the plain catch will catch us.

Of course, keep in mind it would make no sense to put the plain catch before the catch…when for a given exception type since it would get the exception no matter what:

catch (SomeDependencyException ex) {
  // Compiler error, this would catch all instances of SomeDependencyException
}
catch (SomeDependencyException ex)
    when (ex.IsRetryable && tries < MaxAllowedTries) {
  // Impossible
}
catch (SomeDependencyException ex) when (ex.IsRetryable) {
  // Impossible
}

Summary

The catch…when exception filtering in C# 6 gives you a lot of power to clean up complex catch blocks that have to make different handling decisions based on logical conditions in the catch.  By moving those decisions up into the catch…when declaration, we are able to write cleaner exception handling logic that is easier to read and maintain.

Stay tuned for more Little Wonders of C# 6!

Print | posted on Thursday, April 9, 2015 6:36 PM | Filed Under [ My BlogC#Software.NETLittle WondersvNext ]

This article is part of the GWB Archives. Original Author: James Michael Hare

Related Posts