AOP – Implementing try catch in C# with PostSharp
I’ll be honest, I always cringe whenever I have to use try/catch. I do everything I can to avoid it. Besides being a resource hog, try/catch statements clutter code making it harder to read. So naturally I was excited when learning about Aspect-oriented programming (AOP) and PostSharp (the AOP library for C#) that try/catch statements are cross-cutting concerns and can be implemented as aspects.
It is assumed you have the following already installed and licensed:
- Visual Studio
- PostSharp
Step 1 – Create your Visual Studio Project
So to show you how this works, lets get started.
- Create a sample Console Application project in Visual Studio.
- Add a reference to PostSharp.
- Populate the Program.cs with an example exception as shown.
using System; using Common.Aspects; namespace AspectExamples { class Program { static void Main(string[] args) { ThrowSampleExecption(); } private static void ThrowSampleExecption() { throw new ApplicationException("Sample Exception"); } } }
Ok, now we have an example exception, lets show how we can catch this exception, log it, and continue.
Step 2 – Creating an exception aspect
- Right-click on your project and choose Add | Class.
- Give the class a name.
I named my class ExceptionAspect. - Click OK.
- Make the class inherit from OnExceptionAspect.
Note: If you haven’t added a reference to PostSharp, you’ll need to do that now. - Add a string property called Message.
- Add a Type property called ExceptionType.
- Add a FlowBehavior property called Behavior.
The default value is FlowBehavior.Default. - Override the OnException method.
- In the OnException method, create a message and log it.
For this example, I will just log to the Visual Studio Output window. - In the OnException method, set the FlowBehavior to the Behavior property.
- Override the GetExceptionType method and configure it to return the ExceptionType property.
using System; using System.Diagnostics; using PostSharp.Aspects; namespace Common.Aspects { [Serializable] public class ExceptionAspect : OnExceptionAspect { public String Message { get; set; } public Type ExceptionType { get; set; } public FlowBehavior Behavior { get; set; } public override void OnException(MethodExecutionArgs args) { string msg = DateTime.Now + ": " + Message + Environment.NewLine; msg += string.Format("{0}: Error running {1}. {2}{3}{4}", DateTime.Now, args.Method.Name, args.Exception.Message, Environment.NewLine, args.Exception.StackTrace); Debug.WriteLine(msg); args.FlowBehavior = FlowBehavior.Continue; } public override Type GetExceptionType(System.Reflection.MethodBase targetMethod) { return ExceptionType; } } }
Your ExceptionAspect class is complete and ready to use.
Step 3 – Apply the ExceptionAspect
- Add ExceptionAspect as an attribute to the ThrowSampleException method.
- Set the ExceptionType property to type of exception being thrown, which is an ApplicationException in this example.
- Set the Message property to hold a message.
- Set the Behavior property to FlowBehavior.Continue.
using System; using Common.Aspects; using PostSharp.Aspects; namespace AspectExamples { class Program { static void Main(string[] args) { ThrowSampleExecption(); } [ExceptionAspect(ExceptionType = typeof(ApplicationException), Message = "An example exception.", Behavior = FlowBehavior.Continue)] private static void ThrowSampleExecption() { throw new ApplicationException("Sample Exception"); } } }
This is now complete. You have now implemented try/catch as an aspect.
You should take the time to look at your code with .NET Reflector or ILSpy to see what is really being done.
So here is the resulting code for the method accord to ILSpy.
- Notice that our one line of code in the original method is in the try block.
- Notice that in the catch block, the OnException method is called.
- Notice that the catch block also has a switch statement based on the FlowBehavior to determine whether to continue or rethrow, etc.
// AspectExamples.Program private static void ThrowSampleExecption() { try { throw new ApplicationException("Sample Exception"); } catch (ApplicationException exception) { MethodExecutionArgs methodExecutionArgs = new MethodExecutionArgs(null, null); MethodExecutionArgs arg_1F_0 = methodExecutionArgs; MethodBase m = Program.<>z__Aspects.m2; arg_1F_0.Method = m; methodExecutionArgs.Exception = exception; Program.<>z__Aspects.a0.OnException(methodExecutionArgs); switch (methodExecutionArgs.FlowBehavior) { case FlowBehavior.Default: case FlowBehavior.RethrowException: IL_55: throw; case FlowBehavior.Continue: methodExecutionArgs.Exception = null; return; case FlowBehavior.Return: methodExecutionArgs.Exception = null; return; case FlowBehavior.ThrowException: throw methodExecutionArgs.Exception; } goto IL_55; } }
You now can implement pretty much any try/catch block as an Aspect using the ExceptionAspect.
Reusability
One of the main benefits of Aspects is that they take cross-cutting concerns and make them modular. That means that like class files, they are now reusable. Now it is easy to use, link to, or copy and paste your ExceptionAspect class into any other project.
Return to Aspected Oriented Programming – Examples