Last week Fredrik Normén had a couple of nice posts on argument validation using C# extension methods. It got me thinking on a different approach where a method interceptor could handle the validation.

First a short recap on argument validation. I guess everyone have seen or written code with some kind of argument validation, like this for example:

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

To make argument validation easier I have (to a small extent) used a modified version of this design by contract utility class. This utility class lets you write argument validation like this:

public void Add(string value)
{
  Check.Require(value != null, "value should not be null");
    
  ///...
  
  Check.Ensure(Count > 0);
}

So this got me thinking on how you could handle argument validation using method interception. With a method interceptor you could inspect the function arguments checking for validation attributes. I envisioned code looking like this: 

[Ensure(() => this.Count > 0)]
public void Register([NotNull] string name, [InRange(5,10)] int value)
{
  ///...
}

Getting the ensure post condition to work like that will be impossible since you can only use constant expressions in attribute constructors. This is something I think they should look at for C# 4.0, being able to define lambdas in attribute arguments would make interesting scenarios possible (like this). The NotNull and InRange attributes were very simple to implement: 

public abstract class ArgumentValidationAttribute : Attribute
{        
  public abstract void Validate(object value, string argumentName);
}

[AttributeUsage(AttributeTargets.Parameter)]
public class NotNullAttribute : ArgumentValidationAttribute
{
  public override void Validate(object value, string argumentName)
  {
    if (value == null)
    {
        throw new ArgumentNullException(argumentName);
    }
  }    
}    

[AttributeUsage(AttributeTargets.Parameter)]
public class InRangeAttribute : ArgumentValidationAttribute
{
  private int min;
  private int max;

  public InRangeAttribute(int min, int max)
  {
    this.min = min;
    this.max = max;
  }

  public override void Validate(object value, string argumentName)
  {
    int intValue = (int)value;
    if (intValue < min || intValue > max)
    {
      throw new ArgumentOutOfRangeException(argumentName, string.Format("min={0}, max={1}", min, max));
    }
  }
}

This is of course the easy part. It is the actual interceptor that is making the magic happen. This implementation is just a proof of concept, and not optimal from a performance stand point.

public class ValidationInterceptor : IInterceptor
{
  public void Intercept(IInvocation invocation)
  {
    ParameterInfo[] parameters = invocation.Method.GetParameters(); 
    for (int index = 0; index < parameters.Length; index++)
    {
      var paramInfo = parameters[index];
      var attributes = paramInfo.GetCustomAttributes(typeof(ArgumentValidationAttribute), false);

      if (attributes.Length == 0)
        continue;

      foreach (ArgumentValidationAttribute attr in attributes)
      {    
        attr.Validate(invocation.Arguments[index], paramInfo.Name);
      }            
    }

    invocation.Proceed();
  }        
}

To make this interceptor more feasible for production you would probably have to add some smart caching mechanism. This interceptor based argument validation is of course somewhat limited in applicability. You have to use a dynamic proxy generator to get the interceptor functionality and only interface and virtual functions can be intercepted. But it is an interesting idea and shows another nice usage scenario for method interception.

The concept of being able to intercept method calls is something that would be great if it was built into a future version of the CLR runtime. That way static and non virtual methods could be intercepted. It is a nice way to implement cross-cutting concerns (like validation). But until then we will have to live with proxy generators, it works nicely for interfaces:

public interface IRegistrationService
{
  void Register([NotNull] string name, [InRange(5, 10)] int value);

  void Register([NotNullOrEmpty] string name);
}

If you specify the attributes on an interface you do not need to duplicate them on the implementation methods! A small update to the interceptor to support validation of the return value would allow something like this:

public interface IUserRepository
{
  [return: NotNull]
  User GetById([GreaterThanZero] int id);
  
  ///...
}

The syntax for attributes on return values are kind of funky. If Spec# isn't the future (I hope it is) then maybe something like this can be :)

12 comments:

Erik said...

Wow, very cool! The Validation block does something similar for attributes, but I do not think it can validate function arguments!

Anonymous said...

Yes this is the same as something I implemented to fake Aspects in C#.

Victor Sergienko said...

I'm not very happy with runtime check, it adds little to Debug.Assert or whatever.
NotNull should better be a compile-time check.

Sadly, I don't see a way to make attribute work at compile time. I found a pretty (and pretty dead) XC# http://www.resolvecorp.com/Products.aspx which could.

Exactly what I wanted - a post-compile utility that analyzes compiled assemblies and warns if possible-null value is used for not-null variable. Like FxCop.

Sadly, reimplementing it for C#3 should be a lot of work.

Calvin said...

I must say I would also love to see more flexibility with the arguments that can be passed to attributes.

I ran into a similar problem with lambdas in attribute constructors which I solved in the following manner.

public class MyAttWithLambda : Attribute
{

public MyAttWithLambda(Action[object] myAct)
{
myAct.Invoke(this);
}

}

public class MyAdditionalAtt : MyAttWithLambda
{

public MyAdditionalAtt() :
base(c => Console.WriteLine(this.ToString()))
{

}

}

Please forgive in obvious mistakes as I write this from memory...

Its not particularly elegant, but does solve the problem of, really any, types being passed to attributes that cannot be passed in the declarative syntax.

I have also replaced < with [ and > with ] since the guys here deem the [object] ( :P ) statement to be html :)

Torkel Ödegaard said...

Interesting approach to get lambdas in attributes!

michael. said...

Here's a simple implementation that uses PostSharp (http://www.postsharp.org/about/):

[Serializable]
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, AllowMultiple = false)]
public class ArgumentValidator : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionEventArgs eventArgs)
{
ParameterInfo[] pinfo = eventArgs.Method.GetParameters();
foreach (ParameterInfo info in pinfo)
{
foreach (ArgumentValidationAttribute validator in GetCustomAttributes(info, typeof (ArgumentValidationAttribute)))
{
validator.Validate(eventArgs.GetReadOnlyArgumentArray()[info.Position],info.Name);
}
}
base.OnEntry(eventArgs);
}
}

Then you decorate your methods like this:

[ArgumentValidator]
public void DoSomething(string theThing, [NotNull]string theOtherThing)
{ }

Fasty said...

This solution and many others seem to be compensating for poor object oriented language (OOL) designs together with lazy compilers.

The constraints on parameters and return values effectively are derived Types. Consequently our code is underspecified when we use "string" for NonNullable< string > and "int" for RangeLimitedInt< 1,5 >.

In fact, the ubiquity of "int" as a generalized counter from 0..n is a testimonial to the relative immaturity of OOLs.

Vernon said...

Interesting post. Validation Aspects (www.codeplex.com/ValidationAspects) can do all this using PostSharp. Well ok, not attribute funcs! That would be great!

Bill Forney said...

You should check this out... Spec#

Philip Laureano said...

Try LinFu.DesignByContract. It's been out since last year, and it does exactly what you mentioned in this post:

http://www.codeproject.com/KB/cs/LinFu_Part5.aspx

Michael Cowan said...

I know I am just the QA guy .. but we have opted for the simpler method of passing the parameter into a validator and specifying its name.


public static void IsUrlLoaded(string expectedUrl, IE browser)
{
ArgumentValidationService.ThrowIfNullOrEmpty("expectedUrl",expectedUrl);
ArgumentValidationService.ThrowIfNull("browser",browser);
...
...
}

And here is a simple validator:

public static void ThrowIfNull(string paramName, object obj)
{
if (obj == null)
{
throw new ArgumentNullException(paramName);
}
}

Its not fancy, but if you rely solely on the bubble up of exceptions for logging, its better to catch bad data as soon as possible.

Anonymous said...

could you share this: "Small change to support validation of the return value"

What is the required change?

thanks.