1
\$\begingroup\$

I've put together a small fluent utility class for argument validation. Part of the code looks as follows:

public static class ArgumentValidator
{
    private const string defaultArgumentExceptionMessage = "Argument not in expected range.";

    public interface IArgument<T>
    {
        string ParamName { get; }
        T Value { get; }
    }

    private class Argument<T> : IArgument<T>
    {
        public Argument(T argument, string paramName)
        {
            ParamName = paramName;
            Value = argument;
        }

        public string ParamName { get; }
        public T Value { get; }
    }

    public static IArgument<T> UseToVerifyArgumentNamed<T>(this T argument, string paramName = null)
    {
        return new Argument<T>(argument, paramName);
    }

    public static IArgument<T> CompliesWith<T>(this IArgument<T> o, Predicate<T> predicate, string failureMessage = null)
    {
        if (!predicate(o.Value))
        {
            if (failureMessage == null)
                throw new ArgumentException(defaultArgumentExceptionMessage, o.ParamName);
            else
                throw new ArgumentException(failureMessage, o.ParamName);
        }

        return o;
    }

    public static IArgument<T> DoesNotComplyWith<T>(this IArgument<T> o, Predicate<T> predicate, string failureMessage = null)
    {
        if (predicate(o.Value))
        {
            if (failureMessage == null)
                throw new ArgumentException(defaultArgumentExceptionMessage, o.ParamName);
            else
                throw new ArgumentException(failureMessage, o.ParamName);
        }

        return o;
    }

    public static IArgument<T> IsNotNull<T>(this IArgument<T> o)
    {
        if (ReferenceEquals(o.Value, null))
            throw new ArgumentNullException(o.ParamName);

        return o;
    }

    public static IArgument<T> IsSmallerThan<T, Q>(this IArgument<T> o, Q upperBound) where T : IComparable<Q>
    {
        if (o.Value.CompareTo(upperBound) >= 0)
            throw new ArgumentOutOfRangeException(o.ParamName);

        return o;
    }

    public static IArgument<T> IsSmallerThanOrEqualTo<T, Q>(this IArgument<T> o, Q upperBound) where T : IComparable<Q>
    {
        if (o.Value.CompareTo(upperBound) > 0)
            throw new ArgumentOutOfRangeException(o.ParamName);

        return o;
    }

    public static IArgument<T> IsGreaterThan<T, Q>(this IArgument<T> o, Q lowerBound) where T : IComparable<Q>
    {
        if (o.Value.CompareTo(lowerBound) <= 0)
            throw new ArgumentOutOfRangeException(o.ParamName);

        return o;
    }

    public static IArgument<T> IsGreaterThanOrEqualTo<T, Q>(this IArgument<T> o, Q lowerBound) where T : IComparable<Q>
    {
        if (o.Value.CompareTo(lowerBound) < 0)
            throw new ArgumentOutOfRangeException(o.ParamName);

        return o;
    }

    public static IArgument<T> IsInRange<T, Q>(this IArgument<T> o, Q lowerBound, Q upperBound) where T : IComparable<Q>
    {
        if (o.Value.CompareTo(lowerBound) < 0 || o.Value.CompareTo(upperBound) >= 0)
            throw new ArgumentOutOfRangeException(o.ParamName);

        return o;
    }

    public static IArgument<T> IsOutOfRange<T, Q>(this IArgument<T> o, Q lowerBound, Q upperBound) where T : IComparable<Q>
    {
        if (o.Value.CompareTo(lowerBound) >= 0 && o.Value.CompareTo(upperBound) < 0)
            throw new ArgumentOutOfRangeException(o.ParamName);

        return o;
    }
}

And you'd use it in the following way:

void Foo(Bar bar)
{   bar.UseToVerifyArgumentNamed(nameof(bar).IsNotNull().IsInRange(Bar.MinValue, Bar.MaxValue);
    var frobbedBar = Frob(bar);
    frobbedBar.UseToVerifyArgumentNamed(nameof(bar)).CompliesWith(b => b.Frobbed), "Invalid state.");
}

I am, up to a point happy, with the present solution but I'd like any appreciate any advice to improve the implementation. I'd appreciate for instnace ideas for a more succint name for UseToVerifyArgumentNamed method that can still clearly convey what it really does; I just think the present name is too long and there should be a better way to name it.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.