Guarding Your Code
Although code comments are useful, they are easily overlooked and cannot provide a guarantee that the code described will behave a certain way given certain inputs. Listing 1 shows a simple method that can be used to compute the result of an integer division.
Listing 1Dividing Using a User-Provided Divisor
public int Calculate(int divisor, int dividend) { return dividend / divisor; }
While this code will compile and correctly return the result, there is one critical logic flaw in its implementation. One of the basic rules for integer division is that you cannot divide a number by zero. In fact, if you were to try this at compile time, you would receive a Division by constant zero compiler error (Figure 1) and a DvideByZeroException at runtime (Figure 2).
Figure 1 Division by zero compiler error
Figure 2 Division by zero runtime exception
Of course, the Calculate method implies that it follows this rule, but does it really? Nowhere in the implementation is this rule explicitly stated. How can you make this method safer so that it obeys this basic mathematical rule? Because the rule states that the divisor cannot be zero, that intent can be expressed as shown in Listing 2.
Listing 2Guard Statements
public int Calculate(int divisor, int dividend) { if (divisor == 0) { throw new ArgumentOutOfRangeException("divisor"); } return dividend / divisor; }
The if statement in Listing 2 is called a guard statement because it guards against the method continuing to execute if the condition isn't met. Although guard statements are very common, they can become very verbose. Imagine this same method also has a business requirement that both the divisor and dividend are positive numbers greater than zero and that the divisor must be greater than the dividend. Listing 3 shows the necessary guard statements to satisfy these requirements.
Listing 3Multiple Guard Statements
public int Calculate(int divisor, int dividend) { if (divisor <= 0) { throw new ArgumentOutOfRangeException("divisor"); } if (dividend <= 0) { throw new ArgumentOutOfRangeException("divisor"); } if (divisor <= dividend) { throw new ArgumentException("divisor"); } return dividend / divisor; }
As you can see, there are more guard statements than actual implementation, making it difficult to read the intent of the method. Code comments could be used here to clearly indicate the section of code making up the guard statements and the section making up the implementation. However, even that is often not the best solution.
Another, perhaps more serious, problem is that the logic used by the guard statements is inverted from that described by the business requirements. For example, the contract states that both the divisor and dividend must be non-zero positive numbers, but the guard statements test to see if the value is less than or equal to zero.