Argument Validation Utility

A while ago I’ve found a nice way to implement argument validation on the Paint.Net-blog. Its a fluent-interface like this:

public void CallMe(object sender,int size, string name)
{
    Validate.Begin()
        .IsType<Guid>(sender, "sender")
        .BiggerThan(size, 0, "size")
        .IsNotEmpty(name, "name")
        .Check();
    /*IMPL */
}

If only condition fails,

CallMe(Guid.NewGuid(), 2, “”);

you get a normal exception:

System.ArgumentException: The argument ‘name’ cannot be empty

But if more than one condition fails,

CallMe(“I’m not a guid”, 0, null);

you get an exception which reports each violation:

Core.IoC.Utils.MultiException: Validation failed with multiple causes:

The parameter ‘sender’ is not of type: ‘System.Guid’. Instance: ‘I’m not a guid’ of type: System.String

Expected that the argument ‘size’ is bigger than 0 but is is 0

Value cannot be null. Parameter name: name

Its even possible to iterate over the inner exceptions if necessary:

try{
    CallMe("I'm not a guid", 0, null);
} catch(MultiException ex)
{
    foreach (var singleException in ex.InnerExceptions)
    {
        Console.Out.WriteLine(singleException);
    }
}

Now to the clever implementation. All the checks are actually extension-methods, which extend the Validation-class. Now, the initial Validate.Begin()-Method return null! Each validation-method first checks its validation. If it fails, it checks if a Validation-class instance is there or not. If so, it adds its exception, if not, it creates the Validation class instance. The final Check-method then tests if there is actually a Validation class instance and throws the exceptions if necessary. With this clever trick, the validation-checks never allocate any object until there is an exception to throw (more detailed on the Paint.Net-blog)

public static class Validate
{

    public static Validation Begin()
    {
        return null;
    }
}

public sealed class Validation
{
    private readonly List<Exception> exceptions = new List<Exception>(1);

    internal Validation()
    {
    }

    internal IEnumerable<Exception> Exceptions
    {
        get { return exceptions; }
    }

    internal Validation AddException(Exception ex)
    {
        exceptions.Add(ex);
        return this;
    }
}

public static class ValidationExtensions
{
    /// *snip* documentation
    public static Validation IsNotNull<T>(this Validation validation, T theObject, string argumentName) where T : class
    {
        if (theObject == null)
        {
            return (validation ?? new Validation()).AddException(new ArgumentNullException(argumentName));
        }
        return validation;
    }

    /* more checks */

    /// *snip* documentation
    public static Validation Check(this Validation validation)
    {
        if (validation == null) return validation;
        {
            if (validation.Exceptions.Take(2).Count() == 1)
            {
                throw validation.Exceptions.First();
            }
            var messages = new StringBuilder("Validation Failed with multiple causes:");
            messages.AppendLine();
            foreach (var exception in validation.Exceptions)
            {
                messages.AppendLine(exception.Message);
            }
            throw new MultiException(messages.ToString(), validation.Exceptions);
        }
    }
}

[Serializable]
public class MultiException : ArgumentException
{

    internal MultiException(string message, IEnumerable<Exception> exceptions) : base(message)
    {
        InnerExceptions = exceptions;
    }

    /* snip some irrelavant stuff */

    public IEnumerable<Exception> InnerExceptions { get; private set; }

    /* snip serialisation-stuff */
}

As far as I know with .NET 4.0 some kind of contracts will be introduced to the core framework. Then I will start to use those, to benefit from the better tool support.

Tagged on: ,