Click here to Skip to main content
15,906,816 members
Articles / Programming Languages / C#
Article

Using AOP in C#

Rate me:
Please Sign up or sign in to vote.
4.65/5 (15 votes)
9 Jan 20064 min read 106.4K   1.2K   38   14
Shows how to use AOP with C# and the NKalore compiler. This article also has basic exemples of AOP with NKalore, and shows how to use the AFTER, BEFORE, and AROUND advices.

Introduction

Today, AOP is starting to get attention in the .NET community, and right now, there are a lot of frameworks and compilers for .NET to allow programmers to create .NET applications using AOP.

In this article, I will be talking about the NKalore Compiler. NKalore is a compiler based on MCS (Mono C# Compiler), which allows the use of AOP in your C# code with a very simple and intuitive syntax. So, when developing with NKalore, you will use keywords as ASPECT, POINTCUT, AFTER, BEFORE, AROUND, etc.

Getting NKalore

To start using NKalore, I recommend downloading the latest version at SourceForge. The product is still in an Alpha state, but we already can start to play with NKalore and use AOP in C#.

To compile our samples, we will run NKalore in the command-line mode. At the moment, integration with Visual Studio is not available. It is possible to have a basic integration with #Develop, but it’s a subject for another article.

Creating your first AOP application

In this first sample, I will demonstrate the use of the BEFORE advice, so we can imagine that each time before a specified method is called out, the BEFORE advice will be called.

For this, we create a class that has a method called Sum, as you see below:

C#
using System;

namespace Namespace
{
   public class MainClass
   {
      public void Sum(int a, int b)
      {
         Console.WriteLine("Sum = {0}", a + b);
      }
      
      public static void Main()
      {
         new MainClass().Sum(2,3);
      }
   }
}

Now, we are going to create an aspect so every time the "Sum" method is called, it writes a message into the console. Check it out:

C#
using System;

namespace Namespace
{
   public aspect MyAspect
   {
      pointcut SumPointCut void MainClass.Sum(int a, int b);
      
      before SumPointCut(int a, int b)
      {
         Console.WriteLine(
            "The sum of numbers {0} and {1} will be calculated.", a, b);
      }
   }
}

At first, the pointcut is created to identify which methods to "capture". We have put the full name (class name and method name), but we can also use the ‘%’ character to specify the pointcut like a mask.

Then, the BEFORE advice is created. This advice runs every time the Sum method is called. To compile the application, run NKalore in the command window passing the .cs file as a parameter, for example:

mcs before.cs

Here, mcs in the binary name of NKalore.

When you run the application, you will see the following in the console window:

Image 1

Logging everything in your code with BEFORE and AFTER advices

The BEFORE and AFTER advices bring with them some information about the method that is being "captured", all this information is inside the "thisJoinPoint" variable. Now, we are going to use these advices to log every code that is executed in our code.

First, we need a class that has a flow built with methods calling a method, just to demonstrate the use of the log:

C#
namespace Namespace
{
   public class MainCls
   {   
      public MainCls() { a(2); }

      void a(int a) { b("paul", 14); }

      void b(string name, double age) { c(true); }

      void c(bool someFlag) { return; }

      public static void Main()
      {
         new MainCls();
      }
   }
}

And now, the aspect that captures all methods, with any name, returning type, or parameter, and logs every thing into the console with information that is in the "thisJointPoint" variable. See the aspect implemented below:

C#
using System;
using System.Reflection;

namespace Namespace
{
   public aspect LoggingAspect
   {
      pointcut LogPointcut % %(...);

      int level = 0;

      void log(NKalore.Model.JoinPoint joinPoint, 
                  string prefix, bool showParams)
      {
         string pad = new string('·', level - 1);
         Console.WriteLine(pad + prefix + " - " + 
                           joinPoint.MethodInfo);                  
         if (showParams)
         {
            ParameterInfo[] pis = 
                  joinPoint.MethodInfo.GetParameters();
            for (int i = 0; i < pis.Length; i++)
            {
               Console.WriteLine(pad + "\t{0} {1} = {2}", 
                  pis[i].ParameterType, pis[i].Name, 
                           joinPoint.ParamsData[i]);
            }
         }
      }

      after LogPointcut()
      {
         log(thisJoinPoint, "after", false);
         level--;
      }

      before LogPointcut()
      {
         level++;
         log(thisJoinPoint, "before", true);   
      }
   }
}

In the aspect, the AFTER and BEFORE advices increment or decrement the "level" variable to keep the level of the stack. And call the "log" method to show in the console, information about thisJointPoint. The output of the application is the following:

Image 2

The AROUND advice

Another very interesting advice is the AROUND advice. This advice is executed during the execution of the method. So, in reality, when you use this advice, when the method "captured" by the pointcut is executed, your aspect code is executed, and to execute the real method implementation, you need to use the proceed keyword.

To illustrate, we are going to create an aspect with the AROUND advice that calls the implementation of a method (using the proceed keyword). The use of "proceed" is inside a try/catch block, to protect the execution from errors. Take a look at our sample implementation:

C#
using System;
using System.Reflection;

namespace AroundSample
{
   public aspect AroundAspect
   {
      pointcut DoMethods void %.Do%(...);
      
      around void DoMethods()
      {
         try
         {
            Console.WriteLine("Before: {0}", thisJoinPoint.MethodInfo);
            proceed(...);
            Console.WriteLine("After: {0}", thisJoinPoint.MethodInfo);
         }
         catch (Exception ex)
         {
            Console.WriteLine("Error in {0}: {1} ", 
                          thisJoinPoint.MethodInfo, 
                          ex.InnerException.Message);
         }
      }
   }

   public class MainCls
   {
      public void DoSum(int a, int b)
      {
         Console.WriteLine(a + " + " + b + " = " + (a + b));
      }
      
      public void DoMinus(int a)
      {
         Console.WriteLine(a + " - 1  = " + ( a - 1 ));
      }

      public void DoDiv(int a, int b)
      {
         Console.WriteLine(a + " / " + b + " = " + (a / b));
      }

      public static void Main()
      {
         MainCls mainCs = new MainCls();
         mainCs.DoSum(7,3);
         mainCs.DoMinus(5);
         mainCs.DoDiv(5,0);
      }
   }
}

Every method that starts with "Do" is handled by the AROUND advice. First, we log into the console the method that is being executed, writing "before..." and "after...", when the call to the original method is done using proceed keyword. This is made inside a try/catch, block, so if any errors occur (a division by zero will occur in our samples), it will write in the console. The output of this program is:

Image 3

Another possible use of AROUND, is to cache the results of the function into a hash table, and avoid running the same method twice.

* Note that if you only whish to handle the exception, you can use the THROWING advice.

Conclusion

NKalore offers a very simple way to implement AOP into C#, and it’s very intuitive. The project is still in the Alpha stage, but for a while, we can make little programs with it. As roadmap of NKalore says, in the future, probably we will have some more AOP features and integration with Visual Studio, which would be very nice.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Brazil Brazil
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionNkalore compiler working now? Pin
Member 1241382324-Mar-16 1:00
Member 1241382324-Mar-16 1:00 
GeneralI know this is old article, but... Pin
Paulo Zemek13-Nov-09 9:49
Paulo Zemek13-Nov-09 9:49 
GeneralAnother AOP example please Pin
juggler19-Sep-08 1:08
juggler19-Sep-08 1:08 
GeneralRe: Another AOP example please Pin
3iD0LoN31-May-13 11:31
3iD0LoN31-May-13 11:31 
I've got an example for you, it's quite real, in fact it's an issue I'm struggling with right now because of the lack of AOP in standard C#.

My company has just recently bought a development suite + SDK that we will be using to develop our future products. The products we create are specialized in some ways from what the main industry does with the development suite, so there have been debates on whether we want to purchase the source and modify it, or to just build on top of it.

Our company is extremely large, and spans different departments sprinkled all around the globe, and because of this there is no standard of how we approach development of our products. AKA there is no unified standard, and good luck getting people to decide on one when they've been doing things their way for decades. So, with that big of a company size, there are problems with doing either of the two current options.

If we purchase the source and modify it, we now are completely responsible for any bugs that will inevitably come up, whether we created them or the company that vends the suite did, simply because we just won't know if anything we've added has changed the ecosystem of the software.

If we build our own systems on top of the SDK, we really have no way to ensure that our different departments will use it or even be aware of it.

With only OOP principles available in C#, we can't ensure changes that are easy to implement and used consistently. Either we tear apart the source to make it work for us, or we build something that is extremely easy to get around by people who only learn the out-of-the-box way to use the suite.

----------

With all that background in mind, my current task has been to make a tool for our audio department. These are not programmers; they're all music composers. The current work flow requires them to work with programmers if they need to program sound interactions. For example, an interactive sequence of sounds, or events when any audio properties change. My tool, a trigger system of sorts, would be limiting that interaction between programmer and composer, which is has a slow turn around, by making it so that the composers are empowered to program sound events without having to touch any code.

Again, I could rewrite source to incorporate this trigger system as part of the suite, but then liability falls onto me, and I'm tearing up code I really shouldn't, because it could bite me in the ass later. I could write the event system as a wrapper, but what happens if devs add their own sounds outside my system? By circumventing my wrapper, any calls to audio basically go "off the grid" in the scope of my trigger system, which will make this tool useless and may waste more development time as people try to figure out "where that sound is coming from / why can't I make events for it", etc.

However, if I were to use AOP, I could create an aspect that is a trigger. I could then weave this trigger aspect into anything I want to trigger my system. It's simple, it's clean, and modular, just not modular in the way inheritance works. Doing it this way, I don't have to touch the suite source at all, and I can ensure that my system is used even if the nobody realizes it's there. So, if a dev adds a sound, once the project is handed back to composers, it's there in the trigger system without any extra finagling. Another benefit of weaving an aspect into the suite SDK, is that there is no additional learning involved. Devs don't have to learn two APIs because the second library is transparently woven into the other.
GeneralThanks but how does that compare to ... Pin
tech8127-Oct-06 6:07
tech8127-Oct-06 6:07 
QuestionPerformance? Pin
Judah Gabriel Himango14-Sep-06 4:47
sponsorJudah Gabriel Himango14-Sep-06 4:47 
Questionso ? Pin
toxcct9-Jan-06 4:44
toxcct9-Jan-06 4:44 
AnswerRe: so ? Pin
ggeurts18-Jan-06 1:13
ggeurts18-Jan-06 1:13 
GeneralThanks for pointing this out. Pin
Marc Clifton9-Jan-06 4:35
mvaMarc Clifton9-Jan-06 4:35 
GeneralRe: Thanks for pointing this out. Pin
Jerry Holmes18-Jan-06 10:23
Jerry Holmes18-Jan-06 10:23 
GeneralRe: Thanks for pointing this out. Pin
Marc Clifton18-Jan-06 11:31
mvaMarc Clifton18-Jan-06 11:31 
GeneralRe: Thanks for pointing this out. Pin
Jerry Holmes19-Jan-06 3:56
Jerry Holmes19-Jan-06 3:56 
GeneralRe: Thanks for pointing this out. Pin
Roger Alsing10-Feb-06 4:02
Roger Alsing10-Feb-06 4:02 
GeneralRe: Thanks for pointing this out. Pin
dragoncloak18-Mar-15 7:22
dragoncloak18-Mar-15 7:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.