Note: The following article is more an exploratory piece than anything; using any of the code or the approaches discussed in a production setting is not recommended. This article is meant to be as part of a build-up for an ultimate conclusion on the matter.

The Problem

In my previous article on the topic, I got into how we can extend PostSharp’s weaving capabilities in order to add support for special parameter validation aspects.

If you happened to examine any assemblies built using this technology with a static analysis tool like NDepend, you might have had alarms going off complaining about an excessive amount of boxing occurring. This is never a good thing, so let’s dissect this a bit.

The sole concrete validation attribute provided in the example was the NotNullAttribute. The base definition of the validation method employed by this attribute and all other validation attributes looked like the following:

        public abstract void Validate(object target, object value, string parameterName);

So, as we can see, all parameter types must be upcasted to the object type prior to being passed to the above method. Consequently, this also implies that all value type parameter values be boxed prior to being passed to Validate as well.

However, our sole concrete validation attribute (NotNullAttribute) is something we would never decorate a parameter known to be a value type anyway, since value types cannot be null! To do so would be silly, and (if you implemented the compile-time validation routine correctly), would result in a compile-time error.

But even if it is true that no value type parameters are decorated with a validation attribute, you may still have excessive boxing occurring in your assembly. This is because in the example provided in the previous article, box instructions are issued for both value type and generic type parameters.

So, if you’re like me, in that a large number of your methods use generic type parameters and you happen to be validating those parameters, you will have excessive instances of the box instruction in your code. So why does our previous example box generic type parameters? Simply because it takes the lazy way out: those generic type parameters may be bound to value types, they may not, so we box just in case they are.

What happens when you box a generic type parameter that is bound to a value type? Well, it boxes it like normal. What happens when you box a generic type parameter that is bound to a reference type? Nothing, it is essentially a nop. So, in actuality, because boxing will only actually be occurring when we’re binding the generic type parameters to value types, this may not seem like a very big deal to you anymore.

However, we can do better, and avoid boxing altogether for the most part. Among other things, the boxing metric from the analysis tool is rendered useless if we don’t clean up the number of box instructions in the IL. I’m going to go over how to go about doing just this; be aware, though, that my solution is not 100% complete, and I’m still trying to figure out how to support a few “corner” (yeah…not exactly) cases.

The Solution

Let’s take a look at how we can solve this problem.

A Generic Validation Method

One of the simplest ways to address this problem is to have a validation method that will accept a generic type parameter. So…something like the following:

public void GenericValidate<T>(object target, T value, string parameterName)
{...}

We then need to modify our actual weaving code so that it calls this method. Calling generic methods using IL is much more involved than it is when using C#, as the C# compiler handles all the required nasty business for us. All of my weaving code is in a class named ValidationInstructionWriter. One of its responsibilities is emitting calls to the parameter’s validation method, and it is this code that we need to change.

Before we go into those changes, however, let’s go over the general strategy at play here. Or, to put it another way, let’s look at the strategy that arises from some limitations encountered when trying to correct this problem.

Limitations and Things to Do

First of all, I’m not entirely replacing my original validation method with this new generic one. Only when we have a generic type parameter will we be calling the generic validation method. If, instead, we have a non-generic value or reference type, we will actually be calling the original method. Obviously, in the latter case, no box instruction is emitted when the parameter is known to be a reference type, however one is emitted if it is known to be a value type obviously.

Why? Well, the reason is very simple: I don’t know how to call generic methods using the PostSharp SDK unless I already have a generic type parameter. Indeed, this is the very reason as to why I originally only had a the validation method accept object types in the previous article on this topic. I certainly have attempted to figure out how to do this, however I have been unsuccessful so far. It certainly isn’t trivial, and isn’t expected to be, even though it would seem to be fairly standard thing at first (welcome to MSIL, by the way). If anyone out there does know how to do this, I’d love to know.

The PostSharp SDK is an unsupported tool, so I refrain from bothering the PostSharp developers with questions related to it. I’m also going to refrain sharing the problems I encounter when doing this here as well, although I will if there’s some interest. Regardless, I will continue to attempt to figure out how to do it, and I will be sure to let the world know once I figure it out.

In order to insert some balance into this approach, our validation attributes are designed so that the validation method that actually gets overridden in all concrete validation attribute types is the original validation method and not the new generic method. Having to override two different methods would be a bit ridiculous, and would kill this whole thing for me. The generic validation method, thus, is not virtual or abstract; all it does is simply do a direct call to the original validation method.

What’s the point of all of this then? Well, it gets rid of all those extraneous box instructions that would have been littering our code, and it also defers the details of the generic type parameter conversion to the C# compiler. It is also one step closer to the totally correct solution, and it gets the discussion on this topic rolling. Besides, if you remember, if a generic type parameter is bound to a reference type, a box instruction causes no performance hit. However, if it is bound to a value type, then we will get the penalty that comes with boxing. Indeed, that very case is the only one that is negatively impacted by this decision.

Frankly, it’s hardly a setback to have to work with object types instead of unconstrained generic types anyways, which offer no additional capabilities over object typed values. Taking it all into account, this compromise is acceptable under the standards I have for my projects. Frankly, even the original solution is, given of the immense benefits I’ve been discovering from being able to validate parameters in this fashion. Regardless, I’m not completely pleased with the final solution.

Let’s go over some constructs you’re going to see in the code. I’m not going to completely go over everything, as some things were described in the previous article, and other things you’ll need to figure out for yourself.

EntityDescription

I have a custom type I use named EntityDescription. This object basically consists of the  ParameterDeclaration‘s ParameterType, system Type, and name. Assuming you have a field set to the ParameterDeclaration of the parameter we’re validating, it is initialized like so:

EntityDescription description = new EntityDescription(
  _parameter.ParameterType,
  _parameter.ParameterType.GetSystemType(_genericTypeArguments, _genericMethodArguments),
  _parameter.Name);

This is basically just used as a container for commonly needed entity information, and is used outside of the specific scenario we’re covering today.

ValidationCallParameters

Another custom type I use if the ValidationCallParameters type. This is a type specifically purposed for containing the data required for emitting a call to one of our validation methods. I use this type for emitting both parameter and method validation calls, however this article (and the previous article) only concerns parameter validation. Assuming the index of the parameter you are validating is stored in a variable named targetIndex, you would initialize an instance of this type like so:

ValidationCallParameters parameters = new ValidationCallParameters(
    OpCodeNumber.Ldarg,
    targetIndex,
    description.EntityType.IsGenericParameter
        ? Validator.GenericValidationMethod
        : Validator.ValidationMethod);

Note that, for the validation method, I tell it use the GenericValidationMethod only if the parameter type is generic.

Let’s go over the actual code that emits a call to our new validation method.

EmitValidationCall

/// <summary>
/// Emits a call to a method responsible for validating a described entity, using the provided parameters
/// to arm it.
/// </summary>
/// <param name="parameters">The set of data required in order to weave this validation call.</param>
/// <param name="description">A full description of the entity being validated.</param>
public void EmitValidationCall(ValidationCallParameters parameters, EntityDescription description)
{
    // This adds a new instruction sequence to the current block.
    CreateInstructionSequence(NodePosition.Before);

    IMethod validationMethod = FindValidationMethod(parameters, description);

    Writer.EmitInstructionField(OpCodeNumber.Ldsfld, _validatorField);
    Writer.EmitInstruction(!Context.Method.DeclaringType.IsNullable()
                                ? OpCodeNumber.Ldnull
                                : Context.Method.GetFirstParameterCode());

    Writer.EmitInstructionInt16(parameters.TargetOpCode, (short)parameters.TargetIndex);

    if (description.EntityType.IsValueType)
        Writer.EmitInstructionType(OpCodeNumber.Box, description.EntityTypeSignature);

    Writer.EmitInstructionString(OpCodeNumber.Ldstr, description.EntityName);
    Writer.EmitInstructionMethod(parameters.ValidationMethod.GetFirstParameterCode(),
                                    validationMethod);

    Writer.DetachInstructionSequence();
}

The CreateInstructionSequence is a simple routine that behaves in the manner described by the comments above its call. The GetFirstParameterCode extension method is described in my previous article.

As we can see here, we do emit an explicit box instruction if the type of a known value type. If it is not a known value type, however, we won’t box. There really isn’t much new in here from the previous article, except for the call being made to FindValidationMethod, which is a new method introduced to solve this problem. Let’s take a look at that method.

FindValidationMethod

/// <summary>
/// Locates the <see cref="IMethod"/> instance for our validation method.
/// </summary>
/// <param name="parameters">The set of data required for the validation call.</param>
/// <param name="description">Optional. The description of the entity we're validating.</param>
/// <returns>The <see cref="IMethod"/> instance for our validation method.</returns>
private IMethod FindValidationMethod(ValidationCallParameters parameters, EntityDescription description=null)
{
  if (null == description)
      return Context.Method.Module.FindMethod(parameters.ValidationMethod, BindingOptions.Default);

  if (description.EntityType.IsGenericParameter)
  {
      MethodInfo validationInfo = (MethodInfo) parameters.ValidationMethod;

      MethodInfo genericValidationInfo =
          validationInfo.GetGenericMethodDefinition().MakeGenericMethod(description.EntityType);

      return
        Context.Method.Module.FindMethod(
           genericValidationInfo, BindingOptions.RequireGenericInstance & BindingOptions.RequireGenericMask);
  }

  return Context.Method.Module.FindMethod(parameters.ValidationMethod, BindingOptions.Default);
}

This method has an optional parameter for purposes outside of what we’re covering.

So, as we can see here, if we have a generic parameter, we need to call our method in a special way. You may want to add a check in there that ensures ValidationMethod is a generic method before attempting to get a generic method definition from it (which will throw an exception if it isn’t). This is taken care of in our original initialization of the ValidationCallParameters object.

There you go, this is a step on the path to making our validation solution use generics.

(Aphex Twin’s Analord series is some great stuff, by the way).

Matt Weber

I'm the the Senior Software Architect at Emergingsoft where I lead the software development team. I am also the owner of this website. I enjoy well-designed code, independent thought, and the application of rationality in general. You can reach me at matt@badecho.com.

Sorry, the comment form is closed at this time.

   
© 2012-2013 Matt Weber. All Rights Reserved. Terms of Use.