I think I have something like "programmer's OCD". I like my code to be aesthetical and clean, and I want it to be "perfect" (as in handling all possible situations correctly and prettily). Often I find myself spending a lot of time just going over the same areas again and again to see where I can optimize and where I can fool-proof.
So when it comes to try...catch blocks, I experience somewhat of a paranoia regarding what to enclose. I mean, where do I draw the line for what the code should cater to? Tak开发者_如何学Goe file handling, for instance. Should I put, say, every damn file operation in a try...catch block, in case something could have happened (file being locked by someone/something external to the application, disk corruption etc)?
Sometimes it burns my brain feeling that maybe there's something (that I'm not even aware of) that could trip up some piece of code..
Edit:
I'm not talking about using try...catch to cover shitty programming, I'm talking about when it comes to operations and procedures that are otherwise implemented properly, but rely on other factors outside of my control - even if they might be obscure (and this is the point) and only happen under extremely "unlucky" conditions that I haven't foreseen.
File handling is kind of an obvious example. When I tend to get the jitters, it's when I'm wondering about what kind of processing goes on behind the scenes of otherwise built-in functionality, and how it responds to my code.
Here's an example:
Dim serverUrl as String = My.Settings.ServerUrl
There's a disk operation involved there (reading from app.config). Should that be enclosed in a try...catch block? This is what I mean by where does it end.
Fear of memory leaks is another thing. Is it only unmanaged code that poses a threat there? How do I know what is unmanaged code? Is there a list?
More edit:
Another area where I don't feel confident is when there are access restrictions or policies at play somewhere under the hood.
When I read articles and discussions on programming, I see a lot of explanations along the lines of "well, your problem is that when you do a call to X, .Net is internally trying to access so-and-so, and unless your application is running in context type Y or you have privileges Z, it will throw an exception". Which just adds to my paranoia - when it comes to building water tight exception handling. Because I simply don't know all the inner workings of the language/platform, and don't know where to look (without having to devote my life to studying it).
I would love for there to be some form of compendium or concise wiki for this specifically, that would outline which areas of programming that need special attention (file handling etc), with example scenarios, typical challenges and culprits (with solutions), best practice models, programming patterns and, not the least, provide a set of guidelines for mere mortals like me, that unfortunately weren't involved in actually constructing the language and its libraries.
All this in one place, instead of having to track down scattered pieces of information in the language reference or random articles on the net - I don't even know what to look for, in many cases.
As for my particular, current project, it's within the context of a Windows Service. There is no UI, and one of the subtasks I'm working on is to create a robust bootstrapper that handles all problem scenarios gracefully. In this case, it's all about logging - and then either ignoring the exception (if it's trivial enough) - or simply exit! If the problem happens while attempting to log - then what do I do? Just quit - with no trace of what happened? This bootstrapper only logs its startup (after that, the main assembly - which is dynamically loaded - takes over and logs its own stuff, albeit with the same challenges), and does so to a simple "bootstrap.log" file. Would it be better (or a worthwile addition) to have it log to the EventLog? Or is the EventLog another area that could spawn a new world of problems (again, access restrictions etc. Does the EventLog also base itself on disk operations that would need to be "tried and caught"..?)
See? Paranoia.
Should I put, say, every damn file operation in a try...catch block, in case something could have happened (file being locked by someone/something external to the application, disk corruption etc)?
Generally, yes.
Exceptions that must be handled
File operations are inherently unsafe, all hell can break loose when you use it. Consider the case that you’re reading a file stored on an USB storage device and mid-read the user pulls off the USB stick.
This will raise an exception, and you need to guard against that. This is also true in the particular example you mentioned.
On the other hand, it’s usually only the high-level code that needs to handle this kind of error, not the code deep down in your business logic. It is often OK to let a method fail and propagate an exception as the result of an IO failure. It’s merely important to prevent this exception from crashing the application, and notifying the user that something has gone wrong, or try again, or take evasive actions.
Which layers handle exceptions?
An article from 2003 by Ned Batchelder explains which parts of the code should guard against exceptions, and which shouldn’t:
He distributes code into three layers:
- The A layer, adapting the API underneath it,
- The B layer, building pieces of your system, and
- The C layer, combining it all together.
I would choose slightly different layers:
- The low-level layer (consisting of layers A and half B) which builds the low-level API of your system and adapts even lower-level APIs),
- The middle layer that implements the bulk of the business logic (mostly layers B and C) and
- The UI layer that the user interacts with.
In any case, the lowest layer should translate exceptions from the lower-level API into your domain.
The middle layer should let exceptions pass through: no exception handling in the business layer. This should also be the bulk of your code, which means that you don’t need to handle exceptions in most of the code (but there may of course be exceptions to this rule).
And the upper layer should catch exceptions and respond to them, or present them to the user in readable format. A good way of handling this kind of exceptions is by installing a global exception handler, or by offloading work in GUI applications into an extra thread (e.g. a BackgroundWorker) and catching an exception when the thread dies.
Exceptions not to handle
Not all exceptions need to be handled in this way. For example, you shouldn’t try to handle a StackOverflowException
(you can’t!) or an OutOfMemoryException
(you usually can’t).
Such exceptions mean that there is something seriously wrong with either the code or the machine and your application has no chance of recovering from the failure.
精彩评论