Click here to Skip to main content
15,889,867 members
Articles / Programming Languages / C#

A single file argument verification library

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
18 Dec 2011CPOL2 min read 5.5K   2  
A single file argument verification library

I would like to present here a little argument verification library that does not require you to type any string for specifying the name of the parameter you are checking. This makes the library faster to use, not intrusive in the actual method code, and refactor friendly. As a bonus, you can use it by just embedding a single file. We can see below an example, just to get immediately to the point:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NContracts
{
    public class Demo
    {
        
        /// Classical not null verification
//----------> LINE 14
        public void DoSomething(string arg1)
        {
            Contract.Expect(() => arg1).IsNotNull();
        }
        
        /// Asserting an argument greather than another
        public void DoSomething(int min,int max)
        {
            Contract.Expect(() => min)
                .IsLessThan(int.MaxValue)
                .Meet(a => a <= max);
        }
       
        /// Validate an integer argument in a certain range
        public void DoSomething(int arg1)
        {
            Contract.Expect(() => arg1).IsGreatherThan(0)
                                       .IsLessThan(100);
                ;
           
        }
       
        /// Validate an array in length and asserts first element should be zero
        
        public void DoSomething(int[] array)
        {
//--------- LINE46
            Contract.Expect(() => array).Meet(a => a.Length > 0 && a.First() == 0);
        }
       
    }
}

As we can see, there is no magic string at all. All the argument names are guessed, thanks to the metadata contained in the Linq Expression we use. For example, the method at line 14 if called with a null value will report:

Value cannot be null.
Parameter name: arg1 

The same happens to the more complex check we do at line 46, when we write:

C#
Contract.Expect(() => array).Meet(a => a.Length> 0 && a.First() == 0);       

We have a complex predicate do meet, described by a lambda, standing that the input array should have first element zero, and non zero length. Notice that the name of the parameter is array, but we need to use another name for the argument of the lambda (in this case, I used ‘a’), the library is smart enough to understand that ‘a’ actually refers to array, and the error message will report it correctly if the condition is not met. Just to clarify, the message in case of failure would be:

Precondition not verified:((array.First() == 0) AndAlso (ArrayLength(array) > 1))
Parameter name: array       

Well it is not supposed to be a message to an end real user, it is a programmer friendly message, but such validation errors are supposed to be reported to a developer (and end user should not see method validation errors at all, should he?)

Well, Meet is a cutting edge function we can use for complex validations. Out of the box, for simpler cases we have some functions too, as we can see on the IContract interface definition:

C#
public interface IContract<t>
    {
        IContract<t> Is<tt>();
        IContract<t> IsEqual<tt>(TT of) where TT : T,IEquatable<tt>;
        IContract<t> IsGreatherThan<tt>(TT of) where TT : T,IComparable;
        IContract<t> IsGreatherThanOrEqual<tt>(TT of) where TT :T, IComparable;
        IContract<t> IsLessThan<tt>(TT of) where TT : T,IComparable;
        IContract<t> IsLessThanOrEq<tt>(TT of) where TT : T,IComparable;
        IContract<t> IsNot<tt>();
        IContract<t> IsNotEqual<tt>(TT of) where TT : T,IEquatable<tt>;
        IContract<t> IsNotNull();
        IContract<t> IsNotTheSameOf(object of);
        IContract<t> IsNull();
        IContract<t> IsTheSameOf(object of);
        IContract<t> Meet(Expression<predicate<t>> predicate);
    } 

An interesting portion of the codebase proposed is the one renaming the parameter on the lambda expression, to achieve the reported message reflecting the correct offending parameter. It is not so easy because plain string replacement would not work: we can have a parameter named ‘a’, seen in any place in the expression string representation and a plain replacement would resolve in a big mess, furthermore Expressions are immutable. So I found help on StackOverflow, and a reply to this question solved the problem, let see the “Renamer” at work (Thanks to Phil):

C#
static class  PredicateRewriter
        {
            public static Expression<predicate<t>> 
		Rewrite<t>(Expression<predicate<t>> exp
		, string newParamName)
            {
                var param = Expression.Parameter(exp.Parameters[0].Type, newParamName);
                var newExpression = new PredicateRewriterVisitor(param).Visit(exp);

                return (Expression<predicate<t>>)newExpression;
            }

            private class PredicateRewriterVisitor : ExpressionVisitor
            {
                private readonly ParameterExpression _parameterExpression;

                public PredicateRewriterVisitor(ParameterExpression parameterExpression)
                {
                    _parameterExpression = parameterExpression;
                }

                protected override Expression VisitParameter(ParameterExpression node)
                {
                    return _parameterExpression;
                }
            }
        }

Basically, it is a reusable class that takes the new name of the parameter and returns a copy of the input expression with the (single) argument changed.

To improve the library or just use it, please follow/check out the project on Bitbucket. Suggestions and comments are always welcome.


License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Italy Italy
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --