How to validate a method's arguments?

Yesterday I wrote a post about developers that skip validation of arguments. In this post I will give some example of how validation can be done. There are probably better way to do it, and that is why I need your feedback and comments.

The first example is a simple method that uses a simple validation by using “If”.

public string DoSomething(int value1, string value2)
{
    if (value1 > 10 || value1 < 0)
       throw new ArgumentException("The value must be between 0 or 10", "value1");

    if (string.IsNullOrEmpty(value2))
       throw new ArgumentException("The value can't be null or empty", "value2");

    //...
}


I started to use this solution to validate arguments, but what I notice really fast was that I repeat my self over and over again when I created more methods. I solved this by creating an ArgumentHelper class where I add my validation logic.

public static class ArgumentHelper
{
    public static void RequireRange(int value, int minValue, int maxValue, string argumentName)
    {
        if (value > maxValue || value < minValue)
           throw new ArgumentException(string.Format("The value must be between {0} and {1}", minValue, maxValue), argumentName);
    }

    public static void RequireNotNullOrEmpty(string value, string argumentName)
    {
        if (string.IsNullOrEmpty(value))
           throw new ArgumentException("The value can't be null or empty", argumentName);
    }
}

public string DoSomething(int value1, string value2)
{
    ArgumentHelper.RequireRange(value1, 0, 10, "value1");

    ArgumentHelper.RequireNotNullOrEmpty(value2, "value2");

    //...
}


With C# 3.0 we now have Extension Methods, so why  not use Extension Methods to do the validation for a given type? Here is an example where I use Extension methods instead of an ArgumentHelper class:

public static class ArgumentHelperExtension
{
    public static void RequireRange(this int value, int minValue, int maxValue, string argumentName)
    {
        if (value > maxValue || value < minValue)
            throw new ArgumentException(string.Format("The value must be between {0} and {1}", minValue, maxValue), argumentName);
    }


    public static void RequireNotNullOrEmpty(this string value, string argumentName)
    {
        if (string.IsNullOrEmpty(value))
            throw new ArgumentException("The value can't be null or empty", argumentName);
    }
}

public string DoSomething(int value1, string value2)
{
    value1.RequireRange(0, 10, "value1");
    
    value2.RequireNotNullOrEmpty("value2");

    //...
}

As you can see the ArgumentHelper class have no changes, well the "this" key word is added to the first argument in the methods. I can then simply use my argument and call an Extension method that will do the validation.

I recently had a discussion with my friend Roger Alsing and he had some interesting ideas. If we take a look at the Extension Method example, we can see that we add validation extension method to a given type. Let assume we don't want to add several Extension method to the type Int, instead only one method and that method returns a given "interface" which we can extend instead. For example to Int we can have a Extension method called Require and let it return a Numeric<T>, and then we can create Extension methods for the Numeric<T> class instead:

public static string DoSomething(int value)
{
    value1.Require().InRange(0, 10, "value1");

    //...
}

Another interesting solution Roger had is the following:

static void MyMethod(string arne)
{
    RequireNotNull( () => arne);
}

static void RequireNotNull<T>(ArgDelegate<T> del)
{
    T value = del();

    FieldInfo[] fields = del.Target.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
    if (fields.Length != 1)
       throw new NotSupportedException("Invalid argument");

    string argName = fields[0].Name;

    if (value == null)
       throw new Exception(string.Format("Parameter '{0}' may not be null", argName));
}

By using this solution we don't need to pass the name of the argument as a string like in the following solutions:

ArgumentHelper.RequireRange(value1, 0, 10, "value1");

value1.RequireRange(0, 10, "value1");
 
Instead reflection is used to get the name of the argument. This can of course affect performance.

How do you implement your validation code, do you have some cool solution?

Why can't we just have Design By Contract support for C#... ;)

16 Comments

  • I do my validation exactly as you show in your first example:

    if (value == null) throw new ArgumentNullException("value");

    I use code snippets to enter those validation lines quickly.

    When performance is a concern, best you can do is move method calls and exception throwing out of the main flow.

    if (value == null) ThrowHelper.ThrowArgumentNullException("value");

    In the example above the method call will almost never take place (only in case of an exception) and the 'throw ArgumentNullException' is moved out of the public method. Note that the JIT will never inline methods that have exception logic in it, so moving out the exception throwing, means the method could become a candidate for inlining.


  • Steve:
    Is there a reason why you don’t use a helper method for the validation? Because you will repeat yourself. Instead of writing If(value == null) or some other condition, you put the most common into hardcoded methods and that will make sure you can reuse the code and not repeat yourself. You will also reduce the line of code and you don’t need to throw the exception from within the method or by using the ThrowHelper. In your comment you only have one condition, where you check for null. But what if you need a range validation etc, how do you construct your exception message. If you use the first example I have, you will repeat the same message, and if you need to change it, you have several places where you may need to update the message.

  • Nice post,

    @Steven, interesting, I hadn't thought about the fact that the JIT compiler never inlines functions which throws exceptions (it is kind of obvious that it can't do that if you think about it)

  • My sample with the delegates can be changed so that the reflection perf hit does not occur in the normal flow, only when the name is needed..


    ...
    if (value == null)
    {
    FieldInfo[] fields = del.Target.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
    if (fields.Length != 1)
    throw new NotSupportedException("Invalid argument");

    string argName = fields[0].Name;

    throw new Exception(string.Format("Parameter '{0}' may not be null", argName));
    }
    ...

  • The thing I very much dislike about your method of validation that it doesn't expose what the rules are to the caller, so basically I'm stuck calling your code to find out what rules I break. I remember in Java I could create an exception type that a method would throw then I could see either through reflection or the javadocs what exceptions get thrown. Anyone seen anything done like that in .Net?


  • Dave T:
    That is why I want Design By Contract added to C#. But still, a validation that throws an exception when the caller calls it in a wrong way, with a message about what values that are valid for the method. Without the validation the caller will have it difficult to know what values are valid and if that cause the method to fail. Defensive programming is also important to increase the quality of a software.

  • I used to use almost the exact same procedure for getting the argument name via reflection. After running some benchmarks on it, I found that for APIs that are called a non-trivial amount of times, performance is impacted pretty severely. You could create some static fields that are initialized the first the time the method is called with argument names since they are guaranteed never to change while the app is running, but then you really have to start asking yourself how important strong typing for arg names really is.

  • Thanks for nice topic for discussion Fredrik. I prefer "Fluent interface checks" like yours value1.Require().InRange(0, 10, "value1"), but in more readable way: value1.Is().NotNullOrEmpty().InRange(0,10). But the biggest problem with checks like this - methods InRange, Greater, Less and so on need additionas type checking.

  • We have a pair of classes EnsureArgument and AssertArgument which have the same methods, but the first raises exceptions for non-private methods, and the latter raises assertion failures for private methods (the latter is conditional on DEBUG builds).

    e.g. EnsureArgument.IsNotNull("arg", arg);

    Which reads fairly well.

  • I would create an Argument Object, and perform the validation inside it.

    With that many arguments you should probably use an object anyway.

    Schneider

  • Spec# also helps. that way you can add the validation rules to the definition of the actual method. When a client uses the method and passes variables that do not fulfill the rule, it results in a compilation error. How cool is that!

  • I use a helper class too with another readable naming:

    Reject.ifNull(...);
    Reject.ifOutOfBounds(...);
    Reject.if...(...);
    ...

  • What would ArgDelegate look like? When I'm doing reflection to the delegate's Target - it gives me back the method that encapsulates the delegate. Based upon what I can see there's no good way to ensure that the desired field is always field[0] even if the field is properly supplied to the body of the lambda, like in your example.

  • I used a method similar to RequireNotNull in the old C# 2.0 world; I've moved to the following one with C# 3.0's advent:

    static void ThrowIfNull(Expression<Func> value)
    {
    MemberExpression member = value.Body as MemberExpression;
    if (value.Compile().Invoke() == null) throw new ArgumentNullException(
    member != null ? member.Member.Name : string.Empty);
    }

  • Pingback from My Own Fluent Argument Validation Library - .NET Junkie Weblog of a workaholic

  • how to define ArgDelegate in above example?

Comments have been disabled for this content.