6

Conventional wisdom suggests we should only catch the specific exception types we're expecting:

try
{           
    int.Parse(str); //I am aware of TryParse(), this is just for the sake of example
}
catch (FormatException ex)
{
    Console.WriteLine (ex);
}
catch (OverflowException ex)
{
    Console.WriteLine (ex);
}

However, sometimes we don't really care which exception happened (as long as it's not fatal), maybe because we just want to let the user know an error occurred. In these cases we can do something like this:

try
{           
    Foo();
}
catch (Exception ex)
{
    if (IsCriticalException(ex))
    {
        Environment.FailFast("System Error", ex);
    }

    Console.WriteLine ("Error running Foo: {0}", ex);
}   

Where IsCriticalException can be implemented similarly to System.ClientUtils.IsCriticalException. I am generally in favor of this approach for a couple of reasons:

  1. Some methods can throw many exception types and it can be cumbersome to catch them all. For example, File.Open can throw nine different types of exceptions.
  2. Some methods can throw undocumented exceptions. This is either due to lacking documentation, or due to some bug. For example, ICommunicationObject.Close can actually throw ArgumentException due to an obscure bug. Some methods cannot even know statically which exceptions could be thrown by them, since they load other modules / plugins dynamically. Supposedly they could wrap all such "external" exceptions with their own known exceptions, but I believe not all of them do.

The critics of this approach argue that a method's exceptions are part of its contract. If an exception is thrown that is not part of this contract, we should assume its state is corrupt. We will have also discovered a bug in the method (one we otherwise would not have spotted since we'd have swallowed the unexpected exception). We could then relay this bug to the framework's developers, and this is a win - especially if those developers are in our company, so we've "helped ourselves" so to speak.

I admit, the critics present valid points, but I feel they are a bit idealistic. Practically, I think generic non-fatal exception catching makes sense in a lot of situations. Am I making sense?

Related reading:

Community
  • 1
  • 1
Ohad Schneider
  • 36,600
  • 15
  • 168
  • 198
  • Your `File.Open` example is a bad example, IMO. Anything derived from `ArgumentException` points to something that you should have validated before calling `File.Open` in the first place. `PathTooLongException`, `DirectoryNotFoundException`, `FileNotFoundException` all derive from `IOException` and can be handled with a single catch block. That just leaves three types to worry about: `IOException`, `UnauthorizedAccessException`, `NotSupportedException` instead of the nine. –  Jan 06 '14 at 18:20
  • As for `ICommunicationObject.Close`, my personal preference would be to add `ArgumentException` to the list of exceptions to catch (if you know it should never be thrown, and it's only thrown because of a bug, and the impact of that bug makes the exception safe to ignore). –  Jan 06 '14 at 18:22
  • My bet is that any professional that had previously worked with integration will side with you on this - what if you're consuming a method that have no clear/incomplete exception specification, or there's no support for it anymore? - and the method contract approach sounds a bit like wishful thinking to me. Always assume the worst and plan on how to handle it. – OnoSendai Jan 06 '14 at 18:26
  • @hvd regarding `ArgumentNullException` I tend to agree, but I disagree on the ArgumentException. Why should I check the string's length, whitespaces, and invalid characters? I may as well check if the path is not too long. – Ohad Schneider Jan 06 '14 at 19:16
  • @OnoSendai Perhaps it should depend on the method called then. If it's some well-documented method in the BCL, go with the strict approach. Otherwise go with the generic one. – Ohad Schneider Jan 06 '14 at 19:16
  • 1
    @OhadSchneider Sounds sensible to me, I agree. this is clearly not a case of a one-size-fits-all approach. – OnoSendai Jan 06 '14 at 19:32
  • 1
    @OhadSchneider "Why should I check the string's length, whitespaces, and invalid characters?" Generally speaking, `ArgumentException` and derived exceptions mean that the method is telling you "don't pass me that", and if you pass it anyway, you're the one whose code has a bug. I think it may have been better if `File.Open` would throw a different exception for invalid file name characters. –  Jan 06 '14 at 19:43
  • @hvd point taken about `ArgumentException`. Regarding `File.Open` I suppose you're right in this instance, so let's look at another example: `Activator.CreateInstance`. I count 7 non-`ArgumentException` possibilities whose only common ancestor is `SystemException`, which is too far high to catch (for example `OutOfMemoryException` subclasses it as well). Do you believe a caller should have 7 catch clauses even if said caller is only interested to discern between failure and success ? – Ohad Schneider Jan 06 '14 at 20:45
  • 1
    @close-voters I think this is a legitimate design question, and I'm asking it out of real-life issues that arose. Please comment if you disagree. – Ohad Schneider Jan 06 '14 at 20:47
  • @OhadSchneider This may well be due to limited experience, but I haven't had a situation where the exception from `CreateInstance` could be handled at all, where it made sense to ignore the exception and keep going. If you do, and if it makes sense in your case to treat all failure modes equally, then I suppose that is a good example. –  Jan 06 '14 at 20:56
  • @hvd understood. BTW, I thought about what you said regarding `File.Open`, and I'm not sure it is accurate. Sure, you could catch `IOException`, but then you would possibly be swallowing one of the other 4 exceptions in the BCL inheriting from it. I think that if you subscribe to "specific" exception handling school, you need to go all the way. Come to think of it, you may actually need to be *more* specific, validating the exception's type with `GetType()` to make sure it is *exactly* the exception you were expecting... – Ohad Schneider Jan 07 '14 at 10:12

1 Answers1

1

Conventional wisdom suggests we should only catch the specific exception types we're expecting

Actually, I think you should catch exactly those exceptions, that you can handle. There is no use in catching exceptions just to rethrow them and it does not make sense to let those exceptions pass that you would have had a cure for.

In your above example, I think having a catch-all block would have been fine, after all not converting that string is a recoverable error, no matter what exception pops up (except for OutOfMemoryException, which cannot be handled anyway because any attempt at handling would use memory).

nvoigt
  • 75,013
  • 26
  • 93
  • 142
  • In a language which includes `fault` blocks, I would agree that one should only catch exceptions one can hope to resolve. If a method expects to put an object into a temporarily-invalid state and then re-establish its invariants, any unexpected exception which may have occurred while the object's state was invalid should expressly invalidate the object even if it has no hope of handling the exception. Unfortunately, C# really doesn't offer much alternative to catch and rethrow. – supercat Jan 06 '14 at 18:29