Source files for this tutoral: Trace.cs PositiveProperty.cs

Introduction to PostSharp

PostSharp, http://sharpcrafters.com is an AOP development framework. In order to enable attributes, that, effect generated executable code, PostSharp intercepts the C# build process. Well, The specification is not of importance. PostSharp has many interesting capabilities, notably following:
  • Intercepting method invocation, call, success, exception generation.
  • Intercepting properties and interrupting get/set methods.
  • Providing method implementation for abstract and extern methods.
  • Providing implementation of interfaces by applying attributes.
  • Intercepting events and interrupting their invocation.
PostSharp is a commercial software. An installation, and a reference to PostSharp, must be present for development and all programs which use attributes built upon PostSharp, Since PostSharp compiler must be present in order for the aspects to be compiled and applied. Also, in order to execute programs built using PostSharp, a run-time redistributable library of PostSharp must be available, which of course, can be redistributed free of charge. Also, PostSharp itself comes in a Community Edition which is free of charge, but with some limitations, one of important ones is that no type-level aspect is supported in the community edition, although all property-level and method-level aspects are supported. This project targets PostSharp community edition. So in order to develop under this project, a professional edition is not required.
Since this project targets the community edition of PostSharp, the following aspects are possible under this edition:
  • Method Boundary Aspects: Emitting code before/after method invocation. Can also handle exceptions uncaught by the invoked method.
  • Method Interception: Executing code, replacing the original method implementation.
  • Method Implementation: Can implement abstract and extern methods.
  • Location Interception: Emitting code, replacing the original get/set methods associated with properties and fields.
In short, we will briefly explain some examples, and introduce exotic features of PostSharp in action. However, There is something to remember while developing aspects using PostSharp. All attributes develop using PostSharp, must be serializable. This is because in build time, Instances of aspects are created, serialized and transferred to a pipe server executing by PostSharp, which you will be able to see it's icon on the tray. This means that, strictly aspect constructors are called during the build time, and all data which are not serializable, can not emit to the resulting assembly. Also, if there are any infinite loops in the aspect constructors, a build time-out will occur after minutes. So you have to be careful with aspect constructors, because debugging them is tricky.

Method Trace

In this example, we will develop an attribute, using PostSharp. Let's see the code first, and then, we will explain it briefly later.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace ConsoleApplication5
{
    class Program
    {
        [Serializable]
        public sealed class TraceAttribute : PostSharp.Aspects.OnMethodBoundaryAspect
        {
            public override void OnEntry(PostSharp.Aspects.MethodExecutionArgs args)
            {
                Console.Write("Executing " + args.Method.Name + "(");
                for (int i = 0; i < args.Arguments.Count; i++)
                {
                    if (i > 0) Console.Write(", ");
                    Console.Write(args.Arguments.GetArgument(i));
                }
                Console.WriteLine(")");
            }

            public override void OnSuccess(PostSharp.Aspects.MethodExecutionArgs args)
            {
                Console.WriteLine("Return From " + args.Method.Name + ", Return Value: " + args.ReturnValue.ToString());
            }

            public override void OnException(PostSharp.Aspects.MethodExecutionArgs args)
            {
                args.FlowBehavior = PostSharp.Aspects.FlowBehavior.Continue;
                Console.WriteLine(args.Exception.StackTrace);
            }
        }

        [Trace]
        public static int Add(int x, int y)
        {
            if (x == y) throw new ArgumentException();
            return x + y;
        }

        static void Main(string[] args)
        {
            Add(1, 2);
            Add(2, 2);
            Console.WriteLine("Finished successfuly");
        }
    }
}

As you can see, The TraceAttribute class, is the aspect that we have developed using PostSharp. This attribute, When applied to a method, records the following:
  • Method name and arguments on method invocation.
  • Method name and return value on method success.
  • Stack trace on exception and will cause the program to continue progression even though an unhandled exception was occurred. This is exactly just like putting a catch(Exception) block, surrounding the whole method implementation.
As to start, The TraceAttribute class is serialized, so that it can be instantiated, serialized, and transferred to the PostSharp Pipe Server. Also, TraceAttribute class derives from PostSharp.Aspects.OnMethodBoundaryAspect which allows us to put code, before and after the execution of a method. Here are the notably overridable methods of this class:
  • OnEnter: Called on method invocation.
  • OnSuccess: Called when method successfuly exits, and no exception is occured.
  • OnException: Called when method has an unhandled exception.
  • OnExit: Called when the method exits, whether successfully or due to some unhandled exception.

The MethodExecutionArgs class provides run-time information about the executing method. The Instance of the executing method( which is null for static methods ), The reflection properties of the method in question, it's arguments, exception, and return value. we can manipulate and intercept these properties while inside our aspect. We also can control the flow behavior of an unhandled exception. To either ignore it or re-throw it. As you can see the following line will cause the exception to be ignored and just shows the stack trace to the user.
args.FlowBehavior = PostSharp.Aspects.FlowBehavior.Continue;
Manipulation of arguments and the return value should be self-descriptive from above example. So I move forward the next example.

Positive Property Example

In this example, we develop an aspect that can intercept get/set methods of a compiler generated property, to check that the property value is always positive. If we try to assign a non-positive value, a System.ArgumentException is thrown. Let's first see the example code and will be discussing it in more detail.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace ConsoleApplication5
{
    class Program
    {
        [Serializable]
        public sealed class PositivePropertyAttribute : PostSharp.Aspects.LocationInterceptionAspect
        {
            public override void OnSetValue(PostSharp.Aspects.LocationInterceptionArgs args)
            {
                if (System.Collections.Comparer.Default.Compare(args.Value, 0) <= 0)
                {
                    throw new ArgumentException();
                }
                args.ProceedSetValue();
            }

            public override void OnGetValue(PostSharp.Aspects.LocationInterceptionArgs args)
            {
                args.ProceedGetValue();
                if (System.Collections.Comparer.Default.Compare(args.Value, 0) <= 0)
                {
                    throw new ArgumentException();
                }
            }
        }

        [PositiveProperty]
        public static int MyProperty { get; set; }

        static void Main(string[] args)
        {
            MyProperty = 10;
            MyProperty = 1;
            try
            {
                MyProperty = -1;
            }
            catch (ArgumentException)
            {
                Console.WriteLine("MyProperty = -1 threw exception as expected.");
            }
        }
    }
}

As expected, PositivePropertyAttribute is also serializable, but to be able to apply an aspect to properties, we have to derive our aspects from PostSharp.Aspects.LocationInterceptionAspect. Unlike the previous PostSharp.Aspects.OnMethodBoundaryAspect, this aspect replaces the original implementation of get/set methods for the properties. But still we can execute the original methods using LocationInterceptionArgs.ProceedGetValue() and LocationInterceptionArgs.ProceedSetValue() methods with following descriptions:
  • LocationInterceptionArgs.ProceedGetValue() : Calls the original get method for the property, and puts the return value to LocationInterceptionArgs.Value property.
  • LocationInterceptionArgs.ProceedSetValue() : Calls the original set method for the property using the value of LocationInterceptionArgs.Value property, which is initially set to the value user has passed to the set method of property.

As you can see in the following example, We have overriden ProceedGetValue and ProceedSetValue methods, which enables us to intercept property set/get methods which are automatically generated by compiler in this case, and add more logic.

The following examples are precisely examples of AOP and things that can be done using PostSharp.

Download Sample Source
PositiveProperty.cs
Trace.cs

Last edited Aug 30, 2010 at 5:55 AM by arcane_master, version 5

Comments

No comments yet.