tags:

views:

1004

answers:

5

Anyway, I'm a little confused about when to propagate an exception and when to wrap it, and the differences.

At the moment, my understanding tells me that wrapping an exception would involve taking an exception like DriveNotFound (in IO) and then wrap it with the general IOException.

But with the concept of propagating an exception, is this only something that happens if I have an empty catch clause? So in an ASP.NET web app, it would propagate to global.asax. Or in the case of a recently deployed web app, an unhandled HTTPException gave a yellow screen of death and wrote a log to Windows Server (this is a web app I'm rewriting). So the exception happens in a method, it could be handled at the class level, displayed in the page, and then goes up to global.asax or Windows Server.

Why exactly do I want to wrap an exception with a more generic one? The rule is to handle an exception with the most specific type (so DriveNotFound for obviously a drive not found). Also, how would I choose between wrapping and replacing an exception?

Is the exception handling chain just the try and catch (or catches) clauses? I assume from the wording, yes.

Finally, why and how would I want to let an exception propagate up the callstack?

I did read the MS PandP guide on exception handling, but I guess the examples didn't engage me enough to fully understand everything.

This question comes from Enterprise Library the ability to wrap/propagate an exception and etc. It's the propagating I'm not sure about, and the differences in replacing/wrapping an exception.

Also, is it ok to insert complex error handling logic in a catch block (e.g. ifs/elses and things like that).

Thanks

+4  A: 

It's all about conveying the right meaning to your callers. I doubt there's cause to wrap a specific exception in a more generic one - that helps no one.

Consider an API that has nothing to do with file access but accesses a config file behind the scenes. If the config file is not present you might wrap the FileNotFoundException in a ConfigurationException so that the correct problem is conveyed to callers.

why and how would I want to let an exception propagate up the callstack?

You would let an exception propagate if you can't handle it. It's that simple. If there is nothing your code can or should do to work around the exception, then let it propagate. When propagating, be careful how you do so:

throw ex;

is different to:

throw;

The former throws away the old stack trace and creates another from the point the exception is thrown. The latter preserves the original stack trace.

Of course, if you can't handle the exception then you might not bother to catch it in the first place (maybe you want to log though).

HTH, Kent

Kent Boogaart
+4  A: 

There is already a rather good question about this with lots of good answers and discussion. See here:

http://stackoverflow.com/questions/183589/c-windows-forms-best-practice-exception-handling#183626

Echostorm
+2  A: 

I usually do this:

  • Business logic assemblies (dlls) don't handle exceptions unless they are FULLY understood
  • Exceptions that are expected but not handleable are wrapped in a single exception type and are allowed to roll up the call stack (i.e., all the different exceptions that can happen during a database interaction are wrapped in a single RepositoryException and thrown)
  • Unexpected exceptions are allowed to propagate (never include a catch(Exception ex) in a dll)
  • Exceptions are handled only at the last possible moment (i.e., controller for the UI)

Its usually only far up in the call stack that you know exactly what you're doing (what the current business process is) and how to handle exceptions properly.

Will
+8  A: 

No less than 6 questions :-)

But with the concept of propagating an exception, is this only something that happens if I have an empty catch clause?

An exception will propagate upwards until it is caught by a catch block further up the call stack that handles either that specific exception's type or an exception type nearer the base type of that exception's hierarchy. So, for example, all managed exceptions derive from System.Exception, so a catch block that intercepts System.Exception will catch every managed exception.

Why exactly do I want to wrap an exception with a more generic one?

I'm not sure what you mean by "wrap". Do you mean catch an exception, replace it with another, and add the original as the InnerException property of the new exception? Or something else?

I think there's rarely a good reason to replace an exception with a more generic exception. But you can certainly replace an exception with another exception, for one or more of 3 reasons:

  • To hide implementation details from the caller.
  • To improve the level of an abstraction so that it's more meaningful to the caller.
  • To throw a custom exception that's very specific to the problem in hand.

Also, how would I choose between wrapping and replacing an exception?

I'm sorry, but I still don't understand how you're defining these two as different.

Is the exception handling chain just the try and catch (or catches) clauses?

Here are the basics of what happens when an exception is thrown:

  • The CLR walks sequentially down the list of Catch blocks within the local Try...End Try block, looking for a local Catch block with an exception filter matching the exception that was thrown.

  • If a local Catch block has an exception filter that matches the exact exception that was thrown, the code in that Catch block is executed, followed by the code in the Finally block. Then execution continues at the first statement following the End Try.

  • Alternatively, if the exception that was thrown derives from the exception specified by a local Catch block, the same actions happen as described in the second step. For example, an exception filter that catches ArgumentException will also catch exceptions derived from ArgumentException, such as ArgumentNullException, InvalidEnumArgumentException, DuplicateWaitObjectException, and ArgumentOutOfRangeException.

  • If no local Catch block matches the exception that was thrown, the CLR walks back up the call stack, method by method, looking for a Catch block that wants to respond to the exception. If no matching Catch block is found in the call stack, the exception is considered to be unhandled.

  • Alternatively, if a matching Catch block is found somewhere in the call stack, the code in every Finally block between the throw and the catch is executed. This starts with the Finally belonging to the Try block where the exception was thrown and finishes with the Finally in the method below the method where the exception was caught.

  • After this clean-up has been completed for all methods below where the exception was caught, control is transferred to the Catch block that caught the exception, and this code is executed. Next to run is the Finally block of the Try where the exception was caught. Now that the call stack has been unwound and the error clean-up has been completed, the final step is to continue execution at the first statement following the End Try where the exception was caught.

  • If code within a Catch block causes another exception to be thrown, the original exception is automatically appended to the new exception using the InnerException property. In this way, exceptions can be stacked without any loss of information.

  • You should avoid placing clean-up code within a Finally block that might throw an exception, unless that code is within its own Try block. Without this added protection, the CLR behaves as though the new exception was thrown after the end after the Finally block and looks up the call stack for a remote Catch block that wants to respond to the new exception. The original exception will be lost unless the original Catch block saved it.

Finally, why and how would I want to let an exception propagate up the callstack?

Why: Whenever you don't specifically understand the exception and know how to recover from it, you should let it propagate upwards.

How: By only catching exception types that you understand and know how to handle. Occasionally you need the details of any exception in order to do a proper recovery. In this case, you can catch it, do the recovery, then re-throw it by using the throw; statement.

Also, is it ok to insert complex error handling logic in a catch block (e.g. ifs/elses and things like that).

Generally yes, because any new exception caused by code in your Catch block will have the old exception automatically attached to it via the InnerException property. But it's not wise to provoke this mechanism if you can avoid it, so the simpler code you have, the better. Another good reason to keep your Catch code simple is that it often won't go through the same degree of testing as your main-line code.

RoadWarrior
A: 

This is the best resource I've found for implementing a coherent exception handling strategy in .NET

http://msdn.microsoft.com/en-us/library/cc511522.aspx

Christopher