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.