PostSharp 1.0 - First Impressions

Ok so I must've been sleeping under a rock or something, so this is no doubt old news to many people - but PostSharp completely slipped by without me noticing it until now... Having played with it tonight, I'm definitely liking it :)

So far I've just used it for doing some AOP... unlike doing AOP using something like Dynamic Proxy / AspectSharp in Castle, PostSharp gives you a more... personal experience... letting you apply AOP to internal or sealed types, and allowing you to declaratively setup aspects with very little effort... as an example I created a simple little class for recording and playing back methods out of sync... so, first off I have a recorder interface:

public interface IRecorder
{
bool IsPlayback { get; }
void RecordMethod(MethodInfo method, object[] parameters);
}

Following that I implemented a base class for recording the methods, and allowing for playback:

public class BaseRecorder : IRecorder {
private readonly List
_methods = new List();
private bool _isPlayback;

#region IRecorder Members

public void RecordMethod(MethodInfo method, object[] parameters)
{
_methods.Add(new RecordedMethod(method, parameters));
}

public bool IsPlayback
{
get { return _isPlayback; }
}

#endregion

public void Playback()
{
try {
_isPlayback = true;
foreach (RecordedMethod method in _methods) method.Replay(this);
} finally {
_isPlayback = false;
}
}

#region Nested type: RecordedMethod

public class RecordedMethod {
private readonly MethodInfo _method;
private readonly object[] _parameters;

public RecordedMethod(MethodInfo method, object[] parameters)
{
_method = method; _parameters = parameters;
}

public void Replay(object instance)
{
_method.Invoke(instance, _parameters);
}
}

#endregion
}

Pretty basic - then I wrote myself the aspect - it really could not be simpler:

[Serializable]
public class MethodRecordAttribute : OnMethodInvocationAspect
{
public override void OnInvocation(MethodInvocationEventArgs eventArgs)
{
IRecorder recorder = (IRecorder) eventArgs.Delegate.Target;
object[] arguments = eventArgs.GetArguments();
if (recorder.IsPlayback) eventArgs.Delegate.DynamicInvoke(arguments);
else recorder.RecordMethod(eventArgs.Delegate.Method, arguments);
}
}

And finally a demo... so I create a recorder class of my very own and decorate it with the attribute created above:

[MethodRecord]
public class MyRecorder : BaseRecorder
{
public void SayHiTo(string name)
{
Console.WriteLine("Hi {0}", name);
}

public void AddSomeNumbers(int x, int y)
{
Console.WriteLine("{0} + {1} = {2}", x, y, x + y);
}
}

And now a quick demo:

[TestFixture]
public sealed class TryOutPostSharp
{
[Test]
public void RecordAndPlayback()
{
MyRecorder recorder = new MyRecorder();
Console.WriteLine("Starting to record");
recorder.SayHiTo("Alex");
recorder.SayHiTo("Renee");
recorder.SayHiTo("Blog subscribers");
recorder.AddSomeNumbers(10, 20);
Console.WriteLine("Starting to replay");
recorder.Playback();
Console.WriteLine("All done");
}
}

The output is:


Starting to record
Starting to replay
Hi  Alex
Hi  Renee
Hi  Blog subscribers
10 + 20 = 30
All done

Which I think is pretty darn sweet, I can definitely see this having a place in my toolbox in the near future - especially when attempting to uphold DRY in some of my code - anyone using it on production projects in NZ at the moment?

So how does PostSharp pull it off?  Well it post-processes the compiled assembly... it does so by hooking in to an existing extension point in msbuild, so that the post-processor is executed for any project where it references the PostSharp.Public assembly.

All this means that you get to see a couple of extra lines in the build output during compilation:

Compile complete -- 0 errors, 0 warnings
PostSharp 1.0 [1.0.7.261] - Copyright (c) Gael Fraiteur and
Community, 2005-2007.
TryPostSharp ->
C:devexperimentsTryPostSharpTryPostSharpbinDebugTryPostSharp.dll

========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========

But how does it really work? Well if we take a look at the list of 7 approaches to AOP Ayende did ... then it fits into the last two (Compile-time & Run-time IL Weaving).

If you have been living under a rock like myself I'd definitely suggest talking 10 minutes out of your day to give it a quick try.

Written on October 2, 2007