5

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?

1
  • 1
    Of course, exceptions control the flow of the program, in a way that's perfectly suited to the case of a method being unable to fulfill its job, e.g. because of parameters that can't lead to the method's success. Commented Jan 30, 2019 at 20:28

3 Answers 3

9

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.

2
  • The argument that you would save a bunch of processing by erroring out earlier is pretty weak: Is this optimisation of the rare case a pessimisation of the common case? Commented Jan 31, 2019 at 3:51
  • @Deduplicator May be a bit rare, sure, but it is an advantage (though not one specific to throwing exceptions). I don't see how checking validity of all parameters at the beginning of the method is a micro-optimization or a needless check for most cases though. Either you throw a nice exception with invalid parameters, or something else will throw a more obscure one for you...
    – mmathis
    Commented Jan 31, 2019 at 13:59
1

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.

0

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.

8
  • 4
    I can't agree with that answer. If a writeMessageto(...) can't write the message (for whatever reason including invalid parameters), that has to be communicated to the caller, and exceptions are the preferred way to do so. Commented Jan 30, 2019 at 20:17
  • The blog post you mention suggests just a fancy wrapping for the 1970s-style return-a-special-value-in-case-of-failure concept. There are reasons why the industry changed from that style to exceptions. Commented Jan 30, 2019 at 20:23
  • 1
    It only looks like that because it's a functional concept implemented in an object oriented language. Returning an error-code requires lots of if-checking, but functional monadic validation does not. I've added a SESE link to further my point. Commented Jan 30, 2019 at 20:34
  • I think this is a fine answer, in the sense that it gives a valid option for how the asker might do what they need to do. It could be improved by some discussion about when the suggested strategy is or is not appropriate. Commented Jan 30, 2019 at 20:38
  • 2
    I'd think that if other developers are confused enough to think that all exceptions relate to catastrophic events, it's probably time that they learned otherwise. Commented Jan 30, 2019 at 21:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.