Archive

Getting Started With PostSharp

PostSharp Laos makes aspect-oriented programming easy. You can start writing your own aspect in a matter of minutes. Just follow the guide!

1. Before your forget...

Bookmark us with del.icio.us and subscribe to our RSS feed.

2. Download & Install PostSharp

Yes, you have to download and install! But don't worry, it's only 5 MB. Execute the installer (you will need administrative rights) and restart Visual Studio.

3. Open Your Favorite Project

Load one of your projects into Visual Studio 2005 or higher. Any project type will work, but for this example better choose a console application. No matter if you use C#, VB.NET, or another language: it will just work. (Since ASP.NET Web Sites don't use MSBuild, it's a little more difficult with these projects; refer to documentation for details.)

4. Add References To PostSharp

You want to use PostSharp in your project, isn't it? So add a reference to two assemblies: PostSharp.Public.dll and PostSharp.Laos.dll.

Add References to PostSharp

5. Create a New Aspect Class

Create a class and name it TraceAttribute.

public sealed class TraceAttribute : Attribute
{
 private readonly string category;
 
public TraceAttribute( string category )
{
this.category = category;
}
 
public string Category { get { return category; } }
}

6. Derive the Class from OnMethodBoundary

Includes the namespace PostSharp.Laos and make the new class inherit from OnMethodBoundaryAspect. Since this last class derives from System.Attribute, you are actually developing a custom attribute.

[Serializable]
public sealed class TraceAttribute : OnMethodBoundaryAspect
{

7. Implement the Handlers

Once the aspect custom attribute will be applied to a method, this method will invoke the aspect handlers. Do we want to trace methods? So just add trace logic to the handlers!

    public override void OnEntry( MethodExecutionEventArgs eventArgs )
{
Trace.WriteLine(
string.Format( "Entering {0}.{1}.",
         eventArgs.Method.DeclaringType.Name,
eventArgs.Method.Name ),
this.category );
}
 
public override void OnExit( MethodExecutionEventArgs eventArgs )
{
Trace.WriteLine(
string.Format( "Leaving {0}.{1}.",
eventArgs.Method.DeclaringType.Name,
eventArgs.Method.Name ),
this.category );
}

8. Apply the Custom Attribute to Methods

In your project, choose an existing class you want to log and decorate it with our new custom attributes. All methods will be traced. If you prefer to trace some of these methods, don't apply the custom attribute on the class, but on each method.

internal static class Program
{
private static void Main()
{
Trace.Listeners.Add(new TextWriterTraceListener( Console.Out));

SayHello();
SayGoodBye();
}
 
[Trace( "MyCategory" )]
private static void SayHello()
{
Console.WriteLine("Hello, world." );
}
 
[Trace("MyCategory")]
private static void SayGoodBye()
 {
Console.WriteLine("Good bye, world.");
}
}

And what if you have a, say, very large assembly? You surely don't want to decorate every method to log, don't you? Well, no problem at all!

[assembly: Trace( "MyCategory", AttributeTargetTypes = "My.BusinessLayer.*")] 

 

9. We're done! Compile & Execute

That's all, really! If you have a console application, the output should include tracing information.

The output of our program with tracing.

10. How did it work?

Seems magic, huh? Well, to some extent, it is. Open the Output Window of Visual Studio and look at the Build Output: you will see that PostSharp has been invoked. This is the magic wand!

Build output: PostSharp is inserted in the build process.

And now, inspect your assembly using .NET Reflector. You will see how PostSharp enhanced your code:

More tutorials like this

This tutorial is a shorter version of a two-parts article published on codeproject.com. A good way to continue reading about PostSharp!

Features

 PostSharp 1.0PostSharp 1.5
Platforms  
Microsoft .NET 2.0, 3.0, 3.5YesYes
Novell Mono (experimental) Yes
Silverlight 2 Yes
Compact Framework 2.0 Yes
C# (all versions)YesYes
Visual Basic (all versions)YesYes
ASP.NET Web Sites Yes
Integration  
Seamless MSBuild IntegrationYesYes
Command-Line UtilityYesYes
NAnt Task Yes
Extensive Debugging ExperienceYesYes
Installation  
Windows InstallerYesYes
Deployment in Source RepositoryYesYes
Laos  
Pay-as-you-use ComplexityYesYes
Aspects are plain C#/VB classesYesYes
Declarative Build-Time ValidationYesYes
Imperative Build-Time ValidationYesYes
Build-Time InitializationYesYes
Aspect Inheritance Yes
Pluggable Aspect Serialization Yes
Laos Aspect Types  
Around Method YesYes
On ExceptionYesYes
Intercept Method YesYes
Intercept FieldYesYes
Implement Abstract/Extern MethodYesYes
Add InterfaceYesYes
Compound AspectsYesYes
Licensing  
GPL/LGPL License AvailableYesYes
Commercial License AvailableYesYes

As introduced in the previous post, aspect inheritance allows you to apply an aspect to an abstract or virtual method and to have the aspect automatically get propagated to any method overriding the initial method.

The functionality is implemented inside the “custom attribute multicasting” mechanism, so you can use aspect inheritance even for your custom aspects developed using PostSharp Core.

Note that, by default, aspects are not inherited. You have to explicitly enable inheritance on aspect-definition or -usage level (see below).

Lines of Inheritance

Aspect inheritance is supported on the following elements:

  • When applied on interfaces, aspects will be propagated to any class implementing this interface or any other interface deriving this interface.
  • When applied on unsealed classes, aspects will be propagated to any class derived from this class.
  • When applied on abstract or virtual methods, aspects will be propagated to any method implementing or overriding this method.
  • When applied on interface methods, aspects will be propagates to any method implementing that interface semantic.
  • When applied on parameter or return value of an abstract, virtual or interface method, aspects will be propagated to the corresponding parameter or to the return value of derived methods using the method-level rules described above.
  • When applied on an assembly, aspects will be propagated to all assemblies referencing (directly or not) this assembly.

Read between the lines: aspect inheritance is not supported on events and properties, but it is supported on event and property accessors. The reason of this limitation is that there is actually nothing like “event inheritance” or “property inheritance” in MSIL (events and properties have nearly no existence for the CLR: these are pure metadata intended for compilers). Obviously, aspect inheritance is not supported on fields.

Again, I always talk of “aspect inheritance”, but I should talk of custom attribute inheritance.

Enabling Aspect Inheritance

As I said, aspects are not inherited by default. You have to enable inheritance explicitly. This can be done on the level of the aspect class itself using the MulticastAttributeUsageAttribute: all usages of this aspect will have inheritance enabled.

The following example is an attribute that can be applied on parameters or return values, but that is “inherited”:

[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue]
[MulticastAttributeUsage(MulticastTargets.Parameter | MulticastTargets.ReturnValue,
    Inheritance = MulticastInheritance.Strict)]
[RequirePostSharp("Torch.DesignByContract", "Torch.DesignByContract.CheckNonNull")]

public class NonNullAttribute : MulticastAttribute
{
}

Alternatively, it can be done at usage-level. If the Inheritance property is not defined at aspect definition level, you can specify it when you apply the custom attribute:

[Transaction(AttributeInheritance=MulticastInheritance.Strict)]
public abstract void DeleteCustomer( int id );

Note that, once the MulticastAttributeUsageAttribute.Inheritance property has been set, it cannot be overwritten. The deliberate objective of this limitation is to allow aspect developers to specify how their aspect is legally used.

Strict and Multicast Inheritance

As you noticed, the type of properties Inheritance and AttributeInheritance is the enumeration MulticaseInheritance. This enumeration defines three values: None (no inheritance), Strict and Multicast.

To understand the difference between strict and multicast inheritance, remember the original role of MulticastAttribute: to propagate custom attributes along the lines of containment. So, if you apply a method-level attribute to a type, the attribute will be propagated to all the methods of this types (some methods can be filtered out using specific properties of MulticastAttribute or MulticastAttributeUsageAttribute).

The difference between strict and multicasting inheritance is that, with multicasting inheritance (but not with strict inheritance), even inherited attributes are propagated along the lines of containment.

Consider the following piece of code, where A and B are both method-level aspects.

[A(AttributeInheritance = MulticastInheritance.Strict)]
[B(AttributeInheritance = MulticastInheritance.Multicast)]
public class BaseClass
{
  // Aspect A, B.
  public virtual void Method1();
}


public class DerivedClass : BaseClass
{
  // Aspects A, B.
  public override void Method1() {}
  // Aspect B.

  public void Method2();
}

If you just look at BaseClass, there is no difference between strict and multicasting inheritance. However, if you look at DerivedClass, you see the difference: only aspect B is applied to MethodB.

The multicasting mechanism for aspect A is the following:

  1. Propagation along the lines of containment from BaseClass to BaseClass::Method1.
  2. Propagation along the lines of inheritance from BaseClass::Method1 to DerivedClass::Method.

For aspect B, the mechanism is the following:

  1. Propagation along the lines of containment from BaseClass to BaseClass::Method1.
  2. Propagation along the lines of inheritance from BaseClass::Method1 to DerivedClass::Method2.
  3. Propagation along the lines of inheritance from BaseClass to DerivedClass.
  4. Propagation along the lines of containment from DerivedClass to DerivedClass::Method1 and DerivedClass::Method2.

In other words, the difference between strict and multicasting inheritance is that multicasting inheritance applies containment propagation rules to inherited aspects; strict inheritance does not.

Avoiding Duplicate Aspects

If you read again the multicasting mechanism for aspect B, you will notice that the aspect B is actually applied twice to DerivedClass::Method1: one instance comes from the inheritance propagation from BaseClass::Method1, the other instance comes from containment propagation from DerivedClass. To avoid surprises, PostSharp implements a mechanism to avoid duplicate aspect instances. The rule: if many paths lead from the same custom attribute usage to the same target element, only one instance of this custom attribute is applied to the target element. Attention: you can still have many instances of the same custom attribute on the same target element if they have different origins (i.e. they originate from different lines of code, typically). From PostSharp 1.0, you can enforce uniqueness of custom attribute instances by using MulticastAttributeUsage.AllowMultiple.

Inheritance Across Assemblies

All of the above works of course even if the base element (say BaseClass) is defined in another assembly than the derived element (say DerivedClass). Easier to say than to implement!

Under The Hood

From the point of view of performance, there was two problems to address:

  1. Build-time performance: we cannot afford completely scanning every referenced assembly, even indirectly, to see if it does not contain, by chance, an aspect that has to be inherited.
  2. Assembly size: inheritance can cause an attribute to be applied dozens or hundreds of time… If the original aspect has to be duplicated for every of its targets, this would result in a huge assemblies.

Let’s start with the second problem.

If you use a Laos aspect using PostSharp Laos 1.0, you maybe noticed (using Reflector or System.Reflection) that the aspect is not represented as a custom attribute in the transformed assembly. It is, indeed, most of the time useless to have a custom attribute since what you want is a modification of the method behavior. It was possible to change this behavior and force PostSharp to store the custom attribute using MulticastAttributeUsageAttribute.PersistMetaData. But most of the time, this property was false, so the custom attribute was simply not written to the target assembly.

Things are a little more complex with inherited attributes. In order to keep build-time performance to an acceptable level,we do not want to evaluate all multicasting rules for all referenced assemblies again and again. Once we know that an element has an inheritable attribute, we want this custom attribute to be present on the target elements. That’s where the assembly size problem enters the stage: why having dozens of identical copies of the same custom attribute?

So the second problem is addressed by defining (when we can, i.e when PersistMetaData is false) every attribute once, then to use references to this custom attribute. It does not look like standard .NET, and indeed it is not: if you open the assembly using Reflector and goes to a target element, you will see the custom attribute [HasInheritedAttribute( 1234 )]. This is the reference. Where is the definition? Look on the class named “<>MulticastImplementationDetails“. This class has no code, just a list of custom attributes with identifiers. So PostSharp reads this list of custom attributes at build-time and indexes it so that it can resolve references.

The attribute HasInheritedAttributes has a second role related to our first problem: compile-time performance. The last thing we want is to scan all assemblies for types or methods that may have inheritable aspects. Instead of that, we make a progressive search. The HasInheritedAttributes custom attribute (used without constructor) serves like a flag that means that the assembly has at least one inheritable attribute. So if a referenced assembly does not define HasInheritedAttributes, we do not consider it at all. Then, we only look at classes that are actually the direct ancestor of classes defined in our current assembly. If this class has a HasInheritedAttributes attribute, we should scan methods. The same with methods: if it has an HasInheritedAttribute, we should scan parameters.

The custom attribute HasInheritedAttributes has thus two roles. When applied to a target (assembly, type, method, parameter):

  • it means that at least one target under that target has an inheritable attribute, and therefore helps the discovery process,
  • when one or any integers are passed to its constructor, it serves as a reference to a a custom attribute defined under <>MulticastImplementationDetails.

Now that you have read all these details, you can fully understand that aspect inheritance is not only one of the most powerful features of PostSharp, but also one of its more complex!

Happy PostSharping!