Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why use a post compiler?

Tags:

postsharp

I am battling to understand why a post compiler, like PostSharp, should ever be needed?

My understanding is that it just inserts code where attributed in the original code, so why doesn't the developer just do that code writing themselves?

I expect that someone will say it's easier to write since you can use attributes on methods and then not clutter them up boilerplate code, but that can be done using DI or reflection and a touch of forethought without a post compiler. I know that since I have said reflection, the performance elephant will now enter - but I do not care about the relative performance here, when the absolute performance for most scenarios is trivial (sub millisecond to millisecond).

like image 312
Robert MacLean Avatar asked Oct 01 '09 08:10

Robert MacLean


2 Answers

Let's try to take an architectural point on the issue. Say you are an architect (everyone wants to be an architect ;) You need to deliver the architecture to your team: a selected set of libraries, architectural patterns, and design patterns. As a part of your design, you say: "we will implement caching using the following design pattern:"

string key = string.Format("[{0}].MyMethod({1},{2})", this, param1, param2 );
T value;
if ( !cache.TryGetValue( key, out value ) )
{
   using ( cache.Lock(key) )
   {
      if (!cache.TryGetValue( key, out value ) )
      {
         // Do the real job here and store the value into variable 'value'.
         cache.Add( key, value );
      }
   }
}

This is a correct way to do tracing. Developers are going to implement this pattern thousands of times, so you write a nice Word document telling how you want the pattern to be implemented. Yeah, a Word document. Do you have a better solution? I'm afraid you don't. Classic code generators won't help. Functional programming (delegates)? It works fairly well for some aspects, but not here: you need to pass method parameters to the pattern. So what's left? Describe the pattern in natural language and trust developers will implement them.

What will happen?

First, some junior developer will look at the code and tell "Hm. Two cache lookups. Kinda useless. One is enough." (that's not a joke -- ask the DNN team about this issue). And your patterns cease to be thread-safe.

As an architect, how do you ensure that the pattern is properly applied? Unit testing? Fair enough, but you will hardly detect threading issues this way. Code review? That's maybe the solution.

Now, what is you decide to change the pattern? For instance, you detect a bug in the cache component and decide to use your own? Are you going to edit thousands of methods? It's not just refactoring: what if the new component has different semantics?

What if you decide that a method is not going to be cached any more? How difficult will it be to remove caching code?

The AOP solution (whatever the framework is) has the following advantages over plain code:

  1. It reduces the number of lines of code.
  2. It reduces the coupling between components, therefore you don't have to change much things when you decide to change the logging component (just update the aspect), therefore it improves the capacity of your source code to cope with new requirements over time.
  3. Because there is less code, the probability of bugs is lower for a given set of features, therefore AOP improves the quality of your code.

So if you put it all together:

Aspects reduce both development costs and maintenance costs of software.

I have a 90 min talk on this topic and you can watch it at http://vimeo.com/2116491.

Again, the architectural advantages of AOP are independent of the framework you choose. The differences between frameworks (also discussed in this video) influence principally the extent to which you can apply AOP to your code, which was not the point of this question.

like image 110
Gael Fraiteur Avatar answered Sep 18 '22 23:09

Gael Fraiteur


Suppose you already have a class which is well-designed, well-tested etc. You want to easily add some timing on some of the methods. Yes, you could use dependency injection, create a decorator class which proxies to the original but with timing for each method - but even that class is going to be a mess of repetition...

... or you can add reflection to the mix and use a dynamic proxy of some description, which lets you write the timing code once, but requires you to get that reflection code just right -which isn't as easy as it might be, especially if generics are involved.

... or you can add an attribute to each method that you want timed, write the timing code once, and apply it as a post-compile step.

I know which seems more elegant to me - and more obvious when reading the code. It can be applied even in situations where DI isn't appropriate (and it really isn't appropriate for every single class in a system) and with no other changes elsewhere.

like image 12
Jon Skeet Avatar answered Sep 20 '22 23:09

Jon Skeet