Validate the arguments provided to all methods. This is the seventh in a series of posts on Code Gardening - the practice of making small improvements as you see them while you are working.

Everyone knows that good diagnostics make it easier to troubleshoot issues - validating method arguments at the start of a method gives you an opportunity to detect issues earlier, and to give better information.

If you’ve ever needed to track down the cause of a NullReferenceException that’s occurred deep in the bowels of an unfamiliar codebase, you’ll appreciate the importance of these checks.

Consider this method, from when we discussed using full words, not contractions:

public TimeSeries FindByClassification(string classification)
{
    using (var repository = new TimeSeriesRepository())
    {
        using (var transaction = repository.StartTransaction())
        {
            var result = repository.FindByClassification(classification);
            transaction.Commit();
            return result;
        }
    }
}

Passing a null or empty string when searching for a TimeSeries isn’t going to work properly. Depending on the implementation of the repository, the call to FindByClassification might throw an exception or (worse) just return a random result.

To catch this as early as possible, we can add a check to the start of the method to ensure the parameter we’ve passed is acceptable:

public TimeSeries FindByClassification(string classification)
{
    if (String.IsNullOrWhitespace(classification)
    {
        throw new ArgumentNullException(nameof(classification));
    }

    using (var repository = new TimeSeriesRepository())
    {
        using (var transaction = repository.StartTransaction())
        {
            var result = repository.FindByClassification(classification);
            transaction.Commit();
            return result;
        }
    }
}

Now as soon as an inappropriate value for classification is passed to the routine an exception will be thrown, flagging the error sooner. With any luck, this will be closer to the original cause of the problem and will make it easier for someone to solve the underlying issue.

I’ve heard some protest that instrumenting every method in this manner will have a negative impact on performance.

While there is a theoretical performance degradation, these checks are extremely fast to do. In all the performance issues I’ve investigated in my career, none was caused by overly fastidious argument checking of this nature.

As always when it comes to performance issues, don’t guess, measure, then change.

Here’s another example, revisiting the HasCode() method we wrote when discussing helper methods:

public bool HasCode(string code)
{
    code.MustNotBeBlank(nameof(code));
    return String.Equals(Code, code, StringComparison.OrdinalIgnoreCase);
}

Here you can see the use of a simple extension method to validate the parameter easily and succinctly in a single line of code.

The definition of MustNotBeBlank() is

/// <summary>
/// Verify the parameter string is not null or whitespace
/// </summary>
/// <param name="value">Value to check</param>
/// <param name="parameterName">Name of the parameter being checked.</param>
public static void MustNotBeBlank(
    [ValidatedNotNull]this string value, 
	string parameterName)
{
    if (string.IsNullOrWhiteSpace(value))
    {
        var message = String.Format("{0} may not be blank", parameterName);
        throw new ArgumentException( message, parameterName);
    }
}

Similar methods, like MustNotBeNull() and MustNotBeEmpty() are left as an exercise for the reader.

If you like this technique and want to take it to the next level, check out the Code Contracts support in Visual Studio. There are similar tools available for many modern development stacks.

Comments

blog comments powered by Disqus