Question

I was reading on SO and SESE about exceptions and control flow, but I can't seem to determine or figure out if using exceptions to validate parameters is a violation of that guideline.

Suppose I had a method that wrote a message, obviously, I don't want the recipient of the message to be blank. For this example, recipient is a string, but in a more sophisticated program, it might be a class with more parameters.

public void writeMessageto(string recipient, string message){
        // check if recipient and message are blank and throw an 
        // exception if true. 
       ... code to format and write message below....
}

In this particular instance is using exceptions controlling the flow of the program? I don't want to move on to sending a message if both parameters are blank.

Suppose if I had a Message class, in the constructor, to build a valid object I would need to check it's parameters to make sure it isn't blank. If one of the parameters is null or empty throw an exception and the object isn't created. Why is it okay in the Message constructor but not in the method?

If using if statements are better, what happens when validation leads to deeply nested if statements, but throwing an exception would make it more readable?

Was it helpful?

Solution

A method without valid parameters cannot be executed properly, which is indeed an exceptional situation. It is a prime example of correct use of exceptions, and many languages have built-in exception types to handle these cases (ArgumentException, ArgumentNullException, InvalidRangeException in C# for instance).

As you said, you can't really write a message to a recipient if you don't know who the recipient is, thus you throw an exception. Similarly, parameters which are null or out of range prevent you from doing whatever the method is supposed to do, and you throw an exception.

In some cases, if you don't throw an ArgumentNullException, you may wind up throwing a NullReferenceException later on when you try to access that null parameter. Or a DivideByZeroException if one of your parameters is the divisor in a calculation. The argument exception is more descriptive, thus more useful. And, it can save you a bunch of processing to exit the method as soon as possible.

As an alternative, consider what you would do if you didn't throw an exception with a null parameter. The only other real option is to check it for null and then terminate early. With no return type, the caller is then left wondering if the method executed successfully - was the message sent? Or, you add a return type indicating the status, which now has to be checked everywhere the method is called. Much simpler to have the parameter validation all contained where the parameters are used.

That said, you don't want to use exceptions for regular flow control because exceptions are expensive. It is much easier to check if a file exists before reading it than it is to wait for the exception to be thrown when you try to open the file. It leads to cleaner, easier-to-read code. Invalid parameters are (or should be) a rare enough situation that the exceptions aren't incorrectly being used as flow control in those cases.

OTHER TIPS

Two caveats to mmathis's answer:

First, validating values and throwing exceptions can be tedious to read and write; use the best tools available for expressing such behavior (typically the best you have is your language's type system, which will have limits).

Second, by throwing an exception under condition X, you're saying that it was someone else's responsibility to avoid condition X. Depending what tools were available higher at the stack, it may make more sense to "gracefully" handle condition X in the local function.

The heuristic I'd advise is that requirements should be enforced gracefully (without exceptions, or using a very well defined space of exceptions) at the "edges" of a system (the functions that are callable by stuff outside the system), and then within the "body" of your system any violations should cause exceptions/panic as succinctly as possible.
There's a strong case to be made that such exceptions should always be caught again at the edges, although what exactly that looks like will be situational.

This blog-post talks about exactly this. You're looking for monadic validation. Monads provide extra ways to control program-flow, for example a way to "exit" a function in the middle if a validation fails. The blog-post has complete C# examples so excuse me for not repeating them here.

As for using exceptions, I'd suggest against it for two reasons:

  1. This is going to be slow on big projects;

  2. You could end up confusing other developers as some people treat them as describing truly exceptional cases - when something really really bad happens (like an OutOfMemoryException). If it's possible for a user to leave the text blank, then preventing the sending of the empty message is a part of program logic, not an exceptional case.

This SESE also talks about this problem.

Licensed under: CC-BY-SA with attribution
scroll top