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.