PostSharp and iterator blocks – a beautiful combination

I’ve blogged before about the issue of validating parameters in methods implemented with iterator blocks. Tonight, I’ve found a nice way of doing it (in common situations, anyway).

This morning I looked into PostSharp for the first time. PostSharp is an assembly post-processor, giving build-time AOP. I haven’t had any experience with AOP on .NET, but execution time AOP has always felt slightly risky to me. I wouldn’t like to say exactly why, and it may well be mere superstition – but I’m much happier with a tool modifying my code statically after a build than something messing around with it later.

So far, I’m very impressed with PostSharp. It’s a doddle to use and understand, and it has some very neat features. I thought I’d give it an interesting test, seeing what it would make of iterator blocks. I’d already seen its OnMethodBoundaryAspect, which is invoked when a method is called and when it exits. PostSharp works on the compiled code, so the method it sees with iterator blocks is the actual GetEnumerable (or whatever) method, rather than the state machine stuff the C# compiler generates to perform the actual iteration. In other words, the point at which PostSharp gets involved is precisely the point at which we want to validate parameters.

So, initially I had to write an aspect to validate parameter values. In order to make life easier for the developer, I’ve allowed the required parameter to be specified by name. I then find out the parameter position at weave time (post main build, pre execution) – the position as an integer is deserialized a single time when the real code is executed, so I don’t need to look it up from the list of parameters. This also means I can validate that the given parameter name is actually correct, and spot the error at weave time.

Here’s the code for the aspect:

using System;
using System.Reflection;
using PostSharp.Laos;

namespace IteratorBlocks
{
    [Serializable]
    class NullArgumentAspect : OnMethodBoundaryAspect
    {
        string name;
        int position;

        public NullArgumentAspect(string name)
        {
            this.name = name;
        }

        public override void CompileTimeInitialize(MethodBase method)
        {
            base.CompileTimeInitialize(method);
            ParameterInfo[] parameters = method.GetParameters();
            for (int index = 0; index < parameters.Length; index++)
            {
                if (parameters[index].Name == name)
                {
                    position = index;
                    return;
                }
            }
            throw new ArgumentException(“No parameter with name “ + name);
        }

        public override void OnEntry(MethodExecutionEventArgs eventArgs)
        {
            if (eventArgs.GetArguments()[position] == null)
            {
                throw new ArgumentNullException(name);
            }
        }
    }
}

Here’s the “production” code I’ve used to test it:

using System;
using System.Collections.Generic;

namespace IteratorBlocks
{
    class Iterators
    {
        public static IEnumerable<char> GetNormalEnumerable(string text)
        {
            if (text == null)
            {
                throw new ArgumentNullException(“text”);
            }
            foreach (char c in text)
            {
                yield return c;
            }
        }

        [NullArgumentAspect(“text”)]
        public static IEnumerable<char> GetAspectEnhancedEnumerable(string text)
        {
            foreach (char c in text)
            {
                yield return c;
            }
        }
    }
}

And here are the actual test cases:

using System;
using NUnit.Framework;

namespace IteratorBlocks
{
    [TestFixture]
    public class Tests
    {
        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void NormalEnumerableWithNullText()
        {
            Iterators.GetNormalEnumerable(null);
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void AspectEnhancedEnumerableWithNullText()
        {
            Iterators.GetAspectEnhancedEnumerable(null);
        }
    }
}

The NormalEnumerableWithNullText test fails, as I knew it would – but the AspectEnhancedEnumerableWithNullText test passes, showing that the argument validation is eager, despite the actual iteration being lazy. Hooray! Not only that, but I like the declarative nature of the validation check – it leaves the actual implementation of the iterator nice and clean.

The thing which probably impresses me most about PostSharp is how simple it is to use. This code literally worked first time – despite me being a complete novice with PostSharp, having watched the 5 minute video and that being about the limit of my investigation so far. It certainly won’t be the end of my investigation though.

So, why did none of you smart people who have no doubt known about PostSharp for ages actually tell me about it, hmm? I’m always the last to find out…

7 thoughts on “PostSharp and iterator blocks – a beautiful combination”

  1. Well, not entirely new – I mentioned before that I’d like some more DbC in C#. PostSharp seems a very nice way of providing some DbC though – and it relieves this particular pain point really nicely (so long as the parameter validation is simple enough, of course.)

    Jon

    Like

  2. I dunno, this doesn’t seem all that simple to me :-|. I mean, something like “NullArgumentAspect” should be built-in, right? Well, I guess this library is more general-purpose than the DBC goals that we are interested in.

    Ideally, we could refer to parameters without using strings, but I guess that short of new language features, that isn’t going to be possible :(.

    (Note: the above comment is the result of extensive research on C# DBC, consisting of reading like four blog posts by various people :P. So there might be some ignorance there, for sure.)

    Like

  3. This sounds incredibly cool but… what about debugging in Visual Studio? This post-compilation insertion of IL code is likely to throw off the VS debugger. I googled for “debugging” on the project site, and it looks like there are indeed numerous issues. Maybe they’ll get worked out in time, though.

    Also, what’s up with the light bulb after “parameters” in the first code sample? :)

    Like

  4. Chris: Debugging worked well when I tried it. I was able to debug into both the aspect and the iterator block. This was only a tiny test though :)

    Not sure why [i] ended up being a lightbulb, but I’ve changed the variable to index, and it seems to be okay now. Thanks for pointing it out!

    Like

Leave a comment