Jonathan said:
Ever hear of defensive driving? The same technique can be applied to
programming. And for all practical purposes, it should be assumed that
the user will enter invalid filenames.
No one, least of all me, has suggested otherwise.
What percent of the time would they need to enter either invalid
filenames or files that cannot be used, for one reason or another, for
the purpose it was selected before you would decide that your
application should handle that case?
I don't think you have read what I have written very closely. I never
said you shouldn't handle the case. I said that the error case is
exceptional.
Your statement implies that you think that "exceptional" means
"shouldn't handle it". Which is an odd thing for you to think, since
this whole thread is about _handling_ exceptions.
There is NO "percent of time" that a user would have to enter invalid
data for me to think you shouldn't handle that case. The question of
_whether_ the error should be handled is not in disagreement here; it's
_how_ the error should be handled.
With an exception handler, I assume.
Again, you don't seem to be reading the words I wrote.. Look at the
quote you included. I specifically wrote "even absent exception
handling". So, no...NOT "with an exception handler". Your assumption
is exactly opposite of what I wrote.
Yes, I understand the benefits of exception handling and I would use
this approach to an extent, but when taking this approach you are less
likely to get a meaningful error message as it won't be as clear which
of the many sub operations actually failed.
That's not true. In fact, if anything it's much more clear, because a
single error is thrown from the point at which it actually occurred.
The user can easily be provided very specific information about what
went wrong.
Without exception handling, this has always been a basic dilemma in
handling errors. That is, if you are propagating errors back up the
call chain, you have to decide at what point do you actually let the
user know?
Occasionally you'll find an application that maintains an error stack
and allows the user to scroll through all of the errors. So for example
a user might, after providing an invalid filename, be presented with
errors informing him that "the file doesn't exist", and that "the
database couldn't be opened", and that "the application could not switch
to maintenance mode".
But this is rare (and easily implemented with exceptions in any case).
In the vast majority of cases, an application presents the user with a
single error. It's always a design problem to figure out at what level
of error propagation to notify the user. A common solution is to
present a single alert, combining the highest and lowest level errors.
Without exceptions, to implement this the application still needs to
maintain the lowest level error code. It may pass this error code up
through the call chain, as a parameter or return value, or put it in a
global, or do something else with it. But it has to pass the value some
how.
With exceptions, the top level simply receives the exception directly
from that lowest level. It adds its own message information regarding
the high-level operation to the information regarding the low-level
operation that failed. Done. No messing around with passing things
through functions that don't care about errors, trying to overload
return values, or providing by-reference parameters that can chain error
values back up the stack.
There is nothing you can do without exceptions that you cannot do with
exceptions, and vice a versa. The implementation is different, but the
functionality is identical.
More importantly, the scenario you describe -- needing to present a
clear message to the user describing what low-level operation
specifically caused the problem -- is _easier_ to deal with using
exceptions, not harder.
The difference is what happens if you miss an error. It only takes one,
and your entire app blows up. That's the difference and I find it a
substantial one. So I tried to delete a file that doesn't exist. What is
the side effect of not reporting that error? Lost data if you missed the
raised exception.
This is no different than for returned error values. If you don't
bother to check a returned error value and the function failed, your
application blows up. But again, exceptions make things _easier_ for
you, because you only need a single exception handler, at the
highest-level code performing the operation. None of the lower-level
code needs an exception handler except where you need to clean things up
(and you would still need error handling in that situation even without
exception handling).
We obviously have different opinions about what is normal when dealing
with user input and resources.
Again, you don't seem to be reading the words I'm writing. The issue
doesn't have to do with what is "normal when dealing with user input and
resources" (that is, the error handling itself). It has to do with a
philosophical view of what's normal with respect to what the user might
_do_.
The philosophy doesn't change what errors you handle. All of the
"normal" things you might do when dealing with user input, you still do
with exceptions. It simply changes how you view the flow of the code,
and _how_ you handle errors.
For what it's worth, I went through a similar mind-set to the one you
seem to hold now, when C++ exceptions started to become more popular in
the various programming teams I was working in. I already had a
negative view of exceptions, from using setjmp/dojmp in a previous
project; while in hindsight my negative view was entirely my own fault,
because I had failed to adjust my conceptualization of error handling (*
see below), at the time it was still a powerful motivation for me to
reject exceptions.
So when an even more-formalized version of that architecture came along,
in the form of structured exception handling in C++, I was simply not
prepared to embrace it. In spite of people around me trying to explain
the advantages.
I went years after that without really shifting my views. I continued
to write code that simply checked return values. And truth be told,
it's not that I even object to that style of programming today.
However, in .NET you _must_ learn and use exception handling; it is
fundamental to how the Framework works. There is really no point in
being so hostile toward exception handling in any case, but in the .NET
environment that sort of hostility is only going to impede your
productivity. The sooner you relax and open your mind to the benefits
of exception handling, instead of being so darned convinced that you
already know everything there is to know about what's the right and
wrong way to handle errors, the sooner you will be a much happier
programmer.
I apologize if all of this sounds like a new-age Zen sort of "be the
exception" kind of thing. But the truth is a lot of the issue here
really is about you accepting exceptions as a valid and useful paradigm.
Until you do that, you will always have this road-block preventing you
from writing the best .NET code you could.
Pete
(*) Regarding conceptualization of error handling: what I failed to
understand early on about the difference is that when I was using return
values to deal with errors, it made me focus on the function that might
fail. All of my thoughts about error handling were along the lines of
"this function here is what made me have to stop the code from doing
what it was trying to do and recover". When I was dealing with code
that threw exceptions, I failed to get away from this mindset, which
gave me tunnel vision, preventing me from focusing instead on what _my_
code was doing locally.
IMHO, when dealing with exceptions, it is practically always the wrong
thing to do to think about what method might actually fail. Any number
of things might fail. The reason you handle an exception isn't because
some particular method might fail; it's because you need to make sure
that what you're doing locally can recover from a failure.
At the highest level, where a user initiates an operation, this simply
means presenting the user with the error message. At lower levels, this
means cleaning up whatever intermediate steps had already been taken for
the operation. But in all cases, it's not about the thing that failed;
it's about the thing you're doing locally.
IMHO, that's one really nice thing about exceptions. It allows you to
stay focused on the _non-exceptional_ case. Even in the situation where
you are actually handling an exception (and as I've pointed out, one
other advantage of exceptions is that a lot of intermediate code need
not do _any_ error handling, because it has nothing to clean up or
recover from), you only consider the exception in-as-much as it affects
something that you've done in the non-exceptional case.