I've always been one to err on the side of preventing exception conditions by never taking an action unless I am certain that there will be no errors. I learned to program in C and this was the only way to really do things.
Working with C# I frequently see more reactive programing - try to do something and handle exceptions. To me this seems like using exceptions as control statements. The first few times I saw this I dismissed it as bad practice. But over the past few months I've seen it all over the place and just have to wonder - is this accepted/efficient or just an epidemic?
Update: For a bit of clarification most of the exception handling I am seeing is things like
try
{
//open file
}
catch
{
//message box for file not found
}
or even worse
try
{
//open xml
//modify xml (100+ lines of code)
}
catch
{
//message for 'unspecified error'
}
I understand there are times when exception handling is very good to use (such as database connections) but I'm referring to the use of exceptions in place of more 'traditional' control. I asked this because I felt like this programming style was using exceptions as a crutch instead of as a recovery method and wanted to know if this was just something I'd have to learn to expect in the C# world.
As usual, the answer is "it depends", but I subscribe to the "fail fast" philosophy in general.
I prefer to use try/finally (sans catch) unless I can actually do something useful to recover from an exception in a particular block of code. Catching every possible exception isn't worth it. In general, failing fast is preferable to failing silently.
If, on the other hand, you know how to recover from a particular exception, then yes, go do that.
Say you have a file transfer library. It will probably throw an exception if the transfer is interrupted due to a timeout or network failure. That's reasonable. You'll be annoyed if the library just fails silently; checking for a return code is far more error-prone, and not necessarily more readable. But perhaps you have a business rule for sending a bunch of files to a server that you should make at least 3 attempts to transfer the file before giving up and asking for user intervention. In that case, the business logic should handle the exception, try to recover, then do whatever it's supposed to do when the automatic solution fails (alert the user, schedule a later attempt, or whatever).
If you find code that does this:
try
{
// do something that could throw
// ...
}
catch {} //swallow the exception
or:
catch { return null; }
That's probably broken. Sure, sometimes code that you call can throw an exception that you really don't care about. But I often see people do this just so they don't have to "handle" the exception upstream; the practice makes things harder to debug.
Some people consider allowing exceptions to cascade up the chain of responsibility to be bad because you're just "hoping" someone upstream will "miraculously" know what to do. Those people are wrong. Upstream code is often the only place that can know what to do.
Occasionally, I'll try/catch and throw a different, more appropriate exception. However, when possible, a guard clause is better. e,g. if (argument==null) throw new ArgumentNullException();
is better than allowing a NullReferenceException to propagate up the call stack, because it's clearer what went wrong.
Some conditions "should never happen" or "I didn't know that could happen" should probably be logged (see, for example, jboss logging), but can be swallowed before they bring down your application, at least in some cases.
ETA: It is probably broken to take a specific exception and then display a general, ambiguous error message. For your second example above, that sounds bad to me. For your first, "File not found", that may be more reasonable (if you actually catch that specific exception, and not just "everything"), unless you have a better way to deal with that condition somewhere else. Modal Messageboxes are usually a bad "interaction design smell" to me, but that's mostly beside the point.
There are some cases where you are forced to be reactive: Check and operate doesn't always work.
Accessing a file on a disk : is the disk working? does the file exist? can I get read access to the file?
Accessing a database : is the server accepting connections? Are my credentials good? Do the database objects exist? Are the columns named/typed appropriately?
All of these things can change between the check and the operation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With