简体   繁体   中英

Can I/Should I use exceptions to error check my program?

The main example here is copying a word document. I have these lines of code in my program:

try
{
    File.Copy(docTemplatePath, docOutputPath, true);
}
catch (IOException e)
{
    MessageBox.Show(e.ToString());
}

Now I'm trying to build in error checks. I want to make sure that the output Word Document is not open elsewhere before I try to save, because otherwise an exception is thrown.

The way I see it is that the exception is thrown to warn me of an error that is causing the default functionality of the code to not work as expected. In this sense I would feel comfortable catching an exception, reading it to discern the issue (in this case that the document is currently locked/open elsewhere) and thus showing a MessageBox to alert the user to the fact the document they're trying to write to a file that is open elsewhere.

Is this fine to do? I have seen many places over the course of my looking into this where it would seem that this is what exceptions are designed to do, and other places where people seem to think thaat using exception to do stuff like this is hideously against all programming traditions and would prefer to check themselves.

What is the overall consensus?

Thanks

You should use Exceptions to handle exceptional occurrences .

That is; sometimes there will be foreseeable circumstances: A file name entered by a user might not exist, so your code should check File.Exists() before attempting to load it, for example (not relying on an exception).

That file already being in use and locked by something else might be an exceptional situation; it probably is, so that's probably just fine.

IMO, in .Net, exceptions should be used for managed unexpected errors.

For me it's a bad idea to throw an exception for a authentification error (like bad password) but it's a good to use it for manage a database connection error.

So, in few words, i use exceptions in order to manage unexpectable issues and not business issues. The idea of doing every business issues with an exception is more an "over" object oriented approach and is not what it should be use for.

Your example is IMO a good example of the good way to use exception. Globally it's just a "big" try/catch around functionnality or specific tasks.

AFAIK Exceptions are designed for handling things that are out of the developers control like a hardware failure or disconnected network or something similar to that, where the developer might not have the knowledge of the problem,

IMHO it is better to check for the value of divider in the expression int r = val/divider; than to check for the DivideByZeroException

Using Exceptions to control program flow is 'not done'. They should be used only to handle exceptional circumstances, when you don't want your entire application to crash.

That said, sometime it is necessary to use Exceptions. I believe the int.TryParse and other TryParse methods in the .NET Framework use Exceptions, catch them if it doesn't work, and then return false.

If there is no other way of knowing if a Word file is already open, my opinion is that you could use an Exception to do this. The good thing is that you catch a specific exception, because other things could go wrong. The problem is that the IOException could have another source (ie the destination folder is not accesible, etc.). This is true not only for your example, but for all (complex) exception-driven flows. The TryParse example I gave above is fairly simple, so there, it's not so much of a problem.

Conclusion: don't do it, unless it's the only way you can. And then, try to isolate it in a separate method. This way, if you find a better solution, you can always change your implementation. Also, try to catch the most specific exception, and keep in mind that exception can have several sources.

If a routine can satisfy its contract by returning normally, it should do so. If there is no way a routine can return normally without violating its contract, it should throw an exception. If a routine's contract is taken as a given, there's really not much room for judgment in deciding whether the routine should throw an exception. It should return or throw based upon the above simple rule.

The place judgment enters into the equation is in deciding what a routine's contract should be. A useful pattern is to provide both a "Try" version of a routine and a "Do" version; if the "Try" version is unable to perform the requested action because of a reasonably-anticipated problem, it will indicate that by returning some type of error value; if a "Do" version is unable to perform the requested action, for any reason, it will throw an exception.

As a simple example, consider "Integer.TryParse" and "Integer.Parse". If one has a string which may or may not be a valid number, one may call Integer.TryParse. Integer.TryParse will use a returned flag to indicate whether the parse was successful; it will return perfectly happily whether the parse succeeded or not, and the caller is responsible for ensuring that TryParse succeeded before trying to use the returned value. By contrast, Integer.Parse will only return if the number was parsed successfully. If the number wasn't valid, Integer.Parse will throw an exception. Calling code may thus use the value returned by Integer.Parse without having to check whether it succeeded; if Integer.Parse didn't succeed, it wouldn't have returned. Note that it's possible for Integer.TryParse to throw exceptions in some truly exceptional cases (eg if unsafe code had made the passed-in String reference point to some other type of object); the only guarantee is that it won't throw in the reasonably-expected cases (eg it's passed a string like "XYZ" instead of one like "123").

A slight enhancement to the Try/Do pattern is to have a routine accept a delegate which will be invoked if something goes wrong. In many cases, passing a delegate would be overkill, and it's often hard to decide in advance what parameters the delegate should take, but this approach can be advantageous in cases where a decision of whether to muddle on or give up may be determined by outside factors. Such situations arise in communications scenarios, where a program's proper response to a communications hiccup may be to up a message notifying the user that the program is having difficulty and allow the user to hit "cancel", but to have the program retry the operation a few times if the user does not. If the operation that had to be retried was a small portion of the overall operation to be performed, throwing an exception out to the main line would 'permanently' give up on the larger operation, while performing retries without user interaction could force a user to sit annoyingly long for the computer to give up on an operation the user might know won't possibly succeed (eg because the battery just died on the device he was communicating with). Using a callback delegate allows the optimal user interface experience, without tying the communications code to any particular user-interface implementation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM