It's maybe the most exciting feature of PostSharp after PostSharp itself: aspects are now inheritable.
A code sample worthing a hundred words, here is what is now possible:
public interface IDiary { Contact TryFindContact([NonEmpty] string name); [return: NonNull] Contact FindContact([NonEmpty] string name); void Update([NotNull] Contact contact); }
The aspects (here NonEmpty and NonNull) are applied on methods of an interface... but are effective on all implementations of this interface!
(Oh yes, for this to work correctly, you need to download the latest build from https://download.postsharp.org/builds/1.5 -- the CTP 2 contained some bugs that have been solved in the mean time).
The implementation of these aspects does not use Laos, but a dedicated plug-in that generates optimal MSIL instructions. You can download the plug-in using an SVN client from (broken link).
The inheritance feature is indeed implemented at the level of MulticastAttribute, and not directly in Laos. It means that you can use it inside any plug-in. But let's now see an example using Laos.
Simple Invariant Checking
Here is an interesting first example of this feature; it is both simple and useful.
We want to check invariants, and we want to do it simply. With PostSharp 1.5, checking invariants can be as simple as implementing an interface, say IConsistant.
public interface IConsistant { void CheckConsistency(); }
When an object implements the IConsistant assembly, we want the CheckConsistency method to be "automagically" invoked after each non-private instance method.
It's possible by applying a single custom attribute, say [ConsistantAspect] to the IConsistant interface:
[ConsistantAspect] public interface IConsistant
This custom attribute is unbelievably simple:
[AttributeUsage(AttributeTargets.Interface)] [MulticastAttributeUsage(MulticastTargets.Method, TargetMemberAttributes = MulticastAttributes.Public | MulticastAttributes.Protected | MulticastAttributes.Internal | MulticastAttributes.Instance, Inheritance = MulticastInheritance.Multicast)] [Serializable] public sealed class ConsistantAspect : OnMethodBoundaryAspect { public override void OnSuccess(MethodExecutionEventArgs eventArgs) { ((IConsistant) eventArgs.Instance).CheckConsistency(); } }
Basically, we have create an OnMethodBoundaryAspect and we implement the OnSuccess handler that invokes the CheckConsitency method when the target method has successfully completed. The AttributeUsage custom attribute restricts the use of this aspect to interfaces; actually, we will use it only once: on the IConsistant method.
The interesting part if the custom attribute MulticastAttributeUsage on the top of that:
- The property TargetMemberAttributes is old and known: here we define that we want to apply the aspect only to non-private instance methods.
- The property Inheritance is the new and interesting one: the value Multicast means that the aspect should be inherited (from the interface to classes implementing the interface) and then multicast to all methods matching TargetMemberAttributes.
As a result, when we implement the interface and have invariants checked automatically:
class Cashbox : IConsistant { public decimal Balance { get; private set; } public void Debit(decimal amount) { this.Balance -= amount; } public void Credit(decimal amount) { this.Balance += amount; } public void CheckConsistency() { if ( Balance < 0 ) throw new Exception("Invalid balance."); } }
As you can see, no aspect is directly applied on Cashbox or on its methods. Aspects are inherited from IConsistant and then propagated to all public instance methods.
I'll blog more about this feature later.
Happy PostSharping!
-gael