YHO about Try-Catch use.

  • Thread starter Thread starter rhaazy
  • Start date Start date
R

rhaazy

I am looking for some feedback on using try catch statements.
Usually when I start a project I use them for everything, but stop
using them as often after the "meat n' potatos" of the project is
finished.

I am thinking it would be useful to at least have a blanket try-catch
to surround all of the code, and add more to aid in debugging and
catching specific exceptions. I want to change my habits but before I
do I wanted to see if anyone on here had any opinions.

Thanks.
 
My thought is you start with tests. Then you code for the tests. Then you
find you do not need try ... catch everywhere, because you have tested your
code.

One place for try ... catch is your UI layer. You need to show some form of
friendly error, and it is nice to catch problems here. You should also
consider try ... catch when you start getting some strange, unexplained
errrors in your code, as this helps you discover where the true problem is.

As a blanket way of avoiding error messages, try ... catch is a crutch. It
does not improve the quality of code, it just masks it. It can help with
code quality, of course, but if you find you have tons of try ... catches
everywhere, you either are paranoid or you have a code quality issue. If the
former, you are spinning cycles for nothing. If the later, you need to start
examining your code and shoring up the weak points.

--
Gregory A. Beamer
MVP, MCP: +I, SE, SD, DBA

Subscribe to my blog
http://gregorybeamer.spaces.live.com/lists/feed.rss

or just read it:
http://gregorybeamer.spaces.live.com/

********************************************
| Think outside the box! |
********************************************
 
rhaazy said:
I am looking for some feedback on using try catch statements.

They're like champagne: great in moderation, but for some reason people
insist on bringing it into situations where it doesn't add anything to the
occasion, just because they think they should.
Usually when I start a project I use them for everything, but stop
using them as often after the "meat n' potatos" of the project is
finished.
Doing it the other way around has a much better chance of success. If you're
using exception handlers for "everything", does that mean you're writing bad
code that usually fails? Do you feel like you can do something about it when
it does or do you just feel like you've acknowledged that bad things can happen?

An exception handler is a tool for error recovery. You first think about
what sort of error recovery you're going to need and how to best achieve
that, then you write code that keeps it in mind. This will involve strategic
use of exception handlers, but the stress is on "strategic".
I am thinking it would be useful to at least have a blanket try-catch
to surround all of the code, and add more to aid in debugging and
catching specific exceptions.

Putting a try-catch at the highest level is often helpful for two reasons.
One is logging, the other is to make sure the exception doesn't leak outside
your system. For example, if you're writing a web service, you don't want
exceptions to be passed back to the client as-is -- they can expose
embarrassing internals or even sensitive customer information. So you catch
them and return something appropriate, while keeping the full details of
what went wrong in a log for yourself.

Exception handlers for debugging are bogus. Your debugger already stops at
unhandled exceptions, and you can configure it to stop at handled ones too.
You can even configure what exception types it should pay attention to.
You'll have all the information you need at your fingertips. Adding
exception handlers for debugging will just interfere with actual error
handling code.

Wrapping exceptions and re-throwing them with more information, on the other
hand ("this went wrong because of that"), is a better idea, but supporting
debugging should not be the goal in that. Debugging is what you do when
things go wrong and you can still fix them. Your application eventually has
to work then things go wrong and you can't fix them, and your exceptions
should be just as good then. So adding them to have more comprehensive error
reporting is good, but that's not specific to debugging.

There are a few places where you *know* you will need an exception handler
because the operation simply cannot be made to always succeed, you just
don't necessarily know *where*. For example, if you're executing a query on
a database server, this will fail somewhere someday because database servers
are not as available as the CPU your code is running on. So you can't get
around catching an SqlException or a DbException somewhere[*], you just have
to pick the right spot. The right spot will be the one where whatever
application-specific action you were trying to do with that query can be
failed or retried as a whole.

Error recovery is a big topic, and the proper use of exceptions even more
so, so you've asked a nice question for big discussions.

[*]This is not true. You can in fact get away with not catching any
exceptions at all, but this is approach is not trivial to apply. See
http://dslab.epfl.ch/pubs/crashonly/crashonly.pdf, for example. And even
then, it still pays off to have an outer exception handler.
 
rhaazy said:
I am looking for some feedback on using try catch statements.

They're like champagne: great in moderation, but for some reason people
insist on bringing it into situations where it doesn't add anything to the
occasion, just because they think they should.
Usually when I start a project I use them for everything, but stop
using them as often after the "meat n' potatos" of the project is
finished.

Doing it the other way around has a much better chance of success. If you're
using exception handlers for "everything", does that mean you're writing bad
code that usually fails? Do you feel like you can do something about it when
it does or do you just feel like you've acknowledged that bad things can happen?

An exception handler is a tool for error recovery. You first think about
what sort of error recovery you're going to need and how to best achieve
that, then you write code that keeps it in mind. This will involve strategic
use of exception handlers, but the stress is on "strategic".
I am thinking it would be useful to at least have a blanket try-catch
to surround all of the code, and add more to aid in debugging and
catching specific exceptions.

Putting a try-catch at the highest level is often helpful for two reasons..
One is logging, the other is to make sure the exception doesn't leak outside
your system. For example, if you're writing a web service, you don't want
exceptions to be passed back to the client as-is -- they can expose
embarrassing internals or even sensitive customer information. So you catch
them and return something appropriate, while keeping the full details of
what went wrong in a log for yourself.

Exception handlers for debugging are bogus. Your debugger already stops at
unhandled exceptions, and you can configure it to stop at handled ones too.
You can even configure what exception types it should pay attention to.
You'll have all the information you need at your fingertips. Adding
exception handlers for debugging will just interfere with actual error
handling code.

Wrapping exceptions and re-throwing them with more information, on the other
hand ("this went wrong because of that"), is a better idea, but supporting
debugging should not be the goal in that. Debugging is what you do when
things go wrong and you can still fix them. Your application eventually has
to work then things go wrong and you can't fix them, and your exceptions
should be just as good then. So adding them to have more comprehensive error
reporting is good, but that's not specific to debugging.

There are a few places where you *know* you will need an exception handler
because the operation simply cannot be made to always succeed, you just
don't necessarily know *where*. For example, if you're executing a query on
a database server, this will fail somewhere someday because database servers
are not as available as the CPU your code is running on. So you can't get
around catching an SqlException or a DbException somewhere[*], you just have
to pick the right spot. The right spot will be the one where whatever
application-specific action you were trying to do with that query can be
failed or retried as a whole.

Error recovery is a big topic, and the proper use of exceptions even more
so, so you've asked a nice question for big discussions.

[*]This is not true. You can in fact get away with not catching any
exceptions at all, but this is approach is not trivial to apply. Seehttp://dslab.epfl.ch/pubs/crashonly/crashonly.pdf, for example. And even
then, it still pays off to have an outer exception handler.

The situation that bought up this question was an issue with my client
supplying me with bad data. I have to run some sql that performs a
division operation and the denominator is an exchange rate. For some
reason the client sent me some data with some exchange rates set to
0.0000 which makes no sense at all... The only number I should get
thats not an actual exchange rate would be 1.0000. So I got some
divide by zero error that did exactly what you explained, exposed
sensitive data that definately should not be seen, and because of an
error I probably should have tested for, but didn't think to. So I
was debating whether or not to add try-catch to just expose this error
or if I should add it to catch any error. If I add it to catch any
error, than I should add it to the rest of my code too....

Your responses were very informative. I always appreciate the
feedback from the professionals who post on the various google group
programming forums.
 
rhaazy said:
The situation that bought up this question was an issue with my client
supplying me with bad data. I have to run some sql that performs a
division operation and the denominator is an exchange rate. For some
reason the client sent me some data with some exchange rates set to
0.0000 which makes no sense at all... The only number I should get
thats not an actual exchange rate would be 1.0000. So I got some
divide by zero error that did exactly what you explained, exposed
sensitive data that definately should not be seen, and because of an
error I probably should have tested for, but didn't think to.

That last part is the tricky bit, of course. Was it an error or a bug? In
this case, a bug, because of rule #1 when dealing with external data:
external data cannot be trusted and leaving out verification is a bug.

If you cannot tell whether data is valid (the "I didn't think of checking
the exchange rate for 0" problem) then your program isn't really complete.
It should somehow include the knowledge of what's not acceptable input just
as it includes the knowledge of what to do with valid input. These are
usually two sides of the same coin.
So I was debating whether or not to add try-catch to just expose this
error or if I should add it to catch any error. If I add it to catch any
error, than I should add it to the rest of my code too....
In this situation, I'd do this:

1. Add a top-level exception handler that returns "internal system error" or
something equally vague to the client and logs the full exception details
(including stack) to a conspicuous place for you. This handler is not meant
to actually fix any problems -- it's just there to make sure we have a
regular interface to the outside world ("we will always return this or this
or this vague error that tells you nothing but tells me I messed up").

2. Fix the bug(s)! The client can supply any crazy thing it likes, and you
have to be able to handle it without being surprised. Ideally, you set up a
"firewall" where you can say: "beyond this point, I know my data is good
because I've checked it myself or produced it myself from checked data".

Exception handling may or may not come into play for point #2. Certainly,
this is almost certainly wrong:

try {
// Do things with the exchange rate
} catch (SqlException e) {
if (e.Errors[0].Number == 8134) { // Division by zero, can't you tell?
// Wait a minute, why didn't we just validate it before? Are we sure
it's the exchange rate that's causing the problem now, or some other
division in there?
}
}

But this is a possibility:

class RowParseException : Exception {
RowParseException(int rowNumber) { ... }

public int RowNumber { ... }
}

class ExchangeRateOutOfRangeException : RowParseException { ... }

...

if (exchangeRate <= 0) throw new
ExchangeRateOutOfRangeException(rowNumber, string.Format("Exchange rate
'{0}' is out of range.", exchangeRate));

You'd have a handler just before your top-level handler catching
RowParseExceptions and returning the problem to the client.

This is by far not the only way of doing it, which is why it's called an
error strategy and not error boilerplate. Throwing and catching exceptions
is fairly slow, which could be an issue if your service faces heavy load.
Also, the above approach will stop at the first sign of trouble. So you
could also do this (simplified because I'm lazy, don't use public fields but
readonly properties):

class ErrorRow {
Row Row;
RowParseException Error;
}

class ValidateRowsResult {
IEnumerable<Row> GoodRows;
IEnumerable<ErrorRow> ErrorRows;
}

ValidateRowsResult ValidateRows(IEnumerable<Row> inRows) {
List<Row> goodRows = new List<Row>();
List<ErrorRow> errorRows = new List<ErrorRow>();
...
foreach (Row row in rows) {
...
if (row.ExchangeRate <= 0) {
errorRows.Add(new ErrorRow(row, new
ExchangeRateOutOfRangeException(rowNumber, string.Format("Exchange rate
'{0}' is out of range.", exchangeRate))));
continue;
}
...
goodRows.Add(row);
}
return new ValidateRowsResult(goodRows, errorRows);
}

This approach makes it possible to ignore errors and continue. The number #1
selling point of exceptions is that they make it *impossible* to ignore
errors and continue -- you either explicitly handle them or processing is
going to stop. So it depends on what you're trying to achieve.

Here's yet another approach, which looks a lot like the very first one I
turned down but with a crucial difference:

try {
// Do things with an entire row
} catch (SqlException e) {
// We couldn't process this row, we don't know why, the database does
throw new RowStoreException(string.Format("Database error storing row
{0}.", row.Id), e);
}

...

foreach (Row row in rows) {
try {
processRow();
} catch (RowStoreException e) {
// Log the details of the error for ourselves
// Give the client e.Message -- we know it's safe since it's our
exception
}
}

The difference here is that we do not try to figure out exactly *why*
writing to the database failed -- that's the database's problem and
responsibility. We tell the client what we know, which is almost nothing,
and call it a day. This isn't very convenient for either you or the client,
but it's easy to write, and sometimes rapid delivery beats a thorough
middle-tier check.

If you give me more time I'm sure I can think of even more ways to implement
an error strategy here, but only you can decide what's right for your
application. Some people will doubtlessly claim either this way or that is
inherently superior to the others in every situation, but I don't believe
that for a second.
Your responses were very informative. I always appreciate the
feedback from the professionals who post on the various google group
programming forums.

If you really want to show your appreciation, do me a favor and don't call
Usenet "Google Groups". :-) You're not to blame, as Google isn't very candid
about it, but you're posting to a system that's much older than Google
itself, and not all of us access it through Google.
 
<snip />

I'm really having a lot of problems with try-catch myself. I've been
developing like Cowboy suggests (if I understand his premise) and I write a
lot of logical tests in my code to try my best to ensure --my code-- handles
errors. However, its come to my attention that I am really FUBAR ;-) and I
started using try-catch. I'm now having big problems with them and started a
news article just the other day.

I've read here and there but there is no comprehensive training to learn to
debug that I know of and even less to learn where, when and how to use
try-catch. Somethin that should be easy for example...

How do we manage all of the VS2008 and compiler errors such as:

A local variable named 'e' cannot be declared in this scope because it would
give a different meaning to 'e', which is already used in a 'parent or
current' scope to denote something else.

WTF???

And if anybody has any clue why simple code in a try block will not execute
as I've tried discussing on 7/31 I am all ears as I really want to improve
my skills in this context of development...
 
HillBilly said:
How do we manage all of the VS2008 and compiler errors such as:

A local variable named 'e' cannot be declared in this scope because it would
give a different meaning to 'e', which is already used in a 'parent or
current' scope to denote something else.

WTF???

You look at what the compiler message says, and try to fix it. For
instance, that would happen in this code:

public void HandleClick (object sender, EventArgs e)
{
try
{
// Stuff
}
catch (Exception e)
{
// Stuff
}
}

There you've got "EventArgs e" as a parameter and you're trying to
introduce a local variable of the same name. That's not allowed - and
it's a completely separate issue from try/catch handling.
And if anybody has any clue why simple code in a try block will not execute
as I've tried discussing on 7/31 I am all ears as I really want to improve
my skills in this context of development...

Just replied.
 
Back
Top