Reference

You can develop an OnMethodBoundary aspect by deriving the custom attribute OnMethodBoundaryAspect or by implementing the interface IOnMethodBoundaryAspect.

Overview

The OnMethodBoundary aspect results in the target method to be wrapped into a try ... catch ... finally block. You can implement four handlers: OnEntry, executed at the beginning of the block; OnSuccess, executed only when the method is successful (i.e. does not result in an exception); OnException, invoked when the method results in an exception; and OnExit, always executed after method execution (whether the method resulted in an exception or not).

Schematically, the aspect transforms the original method as follows:

int MyMethod(object arg0, int arg1)
{
  OnEntry();
  try
  {
    // Original method body. 

    OnSuccess();
    return returnValue;
  }
  catch ( Exception e )
  {
    OnException();
  }
  finally
  {
    OnExit();
  }
}

Note that this code is only schematic; actually generated instructions are more complex because they have to cope with parameter boxing and control flow modification, among others.

An object of type MethodExecutionEventArgs is passed to every of the four handlers. This object allows you to read and write parameters, get the current exception, set the return value, and modify the control flow.

Access to parameters

The OnMethodBoundary aspect gives you to all method arguments using the methods GetReadOnlyArgumentArray() and GetWritableArgumentArray() of the MethodExecutionEventArgs.

The difference between these two methods is that the last one tells PostSharp that the aspect is allowed to modify arguments. Note that only arguments that were passed by reference (ref or out parameters in C#) can be modified.

Both methods actually return exactly the same array of objects, but future versions of PostSharp may omit to write back arguments if only GetReadOnlyArgumentArray() was invoked in the aspect.

Access to the return value and the current exception

The return value is accessible from the ReturnValue property of the MethodExecutionEventArgs object after successful execution of the original method, i.e. in the OnSuccess and OnExit handlers. Any of these handlers can modify the return value.

The current exception is accessible from the Exception property of the MethodExecutionEventArgs object after failure of the original method, i.e. in the OnException and OnExit handlers. This property is read-only. If you need to replace the current exception by another one, you should throw a new exception from the handler. If you need to ignore the exception, see the section Changing the Control Flow here below.

Composing multiple aspects on the same method

It is perfectly possible to apply multiple OnMethodBoundary aspects on a single method. Different aspects are in complete isolation. They do not need to know about each other.

When multiple aspects are applied on the same method, their order is important. You can influence the aspect order by setting or implementing the AspectPriority property of the aspect.

When we apply an aspect A on a method M, we model the aspected method as two embedded boxes, a box A containing the box M. When we invoke the method, the control flow has to cross the box A and invoke the OnEntry handler. Then the control flow enters the box M. When the method exit, the control flow comes out of the box M and needs to cross the box A again (handlers OnSuccess or OnException, and OnExit).

This box model is useful to understand what happens when many aspects are applied on the same method. Indeed, suppose we have three aspects A1, A2, and A3 on a method M. This configuration is represented by four embedded box: A1 containing A2, which contains A3, which contains M. When the method M is invoked, the control flow has to cross boxes A1, A2 and A3 in direct order. However, then M exists, the control flow crosses the boxes A3, A2 and A1, i.e. in reverse order of priority.

To summarize, when multiple aspects are applied to the same method, OnEntry handlers are invoked in increasing order of priority, but OnSuccess, OnException and OnExit are invoked in decreasing order of priority.

Sharing state between handlers

It is sometimes useful to share state between handlers of the same method execution. For instance, in a caching aspect, the OnEntry handler may compute the cache key, which will then be used by the OnSuccess handler.

The OnMethodBoundary aspect supports a state sharing mechanism that is both thread-safe and reentrant. Indeed, different handlers of the same aspect can store state in the MethodExecutionTag property of the MethodExecutionEventArgs object. If this property is set by a handler, it will be available to any handler of the same aspect invoked later, for the same method execution.

Changing the Control Flow

The default behavior of an aspect method, after a handler has been executed, is to continue its natural execution. The goal of this strategy is that empty handlers should not affect the method execution.

Sometimes however, a more complex behavior is required. For instance, a caching aspect may want to skip the method execution if the method result has already been found in cache by the OnEntry handler. Or an exception handler may want to ignore the exception caught in the OnException handler.

This feature is exposed by the FlowBehavior property of the MethodExecutionEventArgs object. You can set this property in any handler in order to modify the control flow after the execution of that handler. Possible values are documented in the reference documentation of the FlowBehavior enumeration.