NKalore

    NKalore是一款编程语言,它扩展了C#允许在.net平台使用AOP。NKalore的语法简单、直观,它的编译器是基于MonoC#编译器(MCS)。NKalore目前只能在命令行或#Develop内部使用。NKalore兼容公共语言规范CLS(CommonLanguage Specification),它可以在任何.NET开发环境中使用,包括微软的Visual Studio .NET。


Roadmap

For version, 0.2
we are planning to implement the following features, some of those features are already implemented.
               
  •                         Support for advices in properties
  •                                 Extends the After advice, with support for
    •                                                 After returning
    •                                                 After throwing
                           
  •                         Synchronize the source code with latest MCS version
  •                                 More information in thisJoinPoint
    •                                                 ReturnValue of the method
                           
  •                         Allow more complex filters in pointcut
  •                                 Allow aspect to introduce members into classes
    •                                         Attributes
    •                                         Interfaces
    •                                         Method
    •                                         Fields
    •                                                 Etc
                           
  •                                 Start to work with some kind of integration with IDEs
    •                                         MS VS.NET
    •                                                 #Develop


Samples
                                                                                                                                                           
  • around1.cs
    A simple demostration for around advice.                                                               
         
    using System;

    namespace AroundSample
    {
            public aspect AroundAspect
            {
                    pointcut MyPointCut int MainCls.Sum(int i);

                    around int MyPointCut(int i)
                    {
                        Console.WriteLine("Inside Around");
                    Console.WriteLine("Before SUM");
                            int result = proceed(i);
                            Console.WriteLine("After SUM");
                            Console.WriteLine("Value in around: " + result);
                            return result;
                    }
            }

            public class MainCls
            {
                    public int Sum(int i)
                    {
                            Console.WriteLine("Inside Sum");
                            return i+1;
                    }

                    public static void Main()
                    {
                            MainCls mainCs = new MainCls();
                            Console.WriteLine(mainCs.Sum(1));
                    }
            }
    }
                                                                                           
                                    Program output:
                                   
  • logging.cs                                                               
    Log all methods executed in the application, the log also includes the names                                                                and values of the parameters of the method.                                                               
         
    using System;
    using System.Reflection;

    using NKalore.Model;

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

                    int level = 0;

                    void log(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.ParameterType, pis.Name, joinPoint.ParamsData);
                                    }
                            }
                    }

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

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

            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();
                    }
            }
    }

                                                                                                                                           
                                    Program output:
                                   
  • exception5.cs                                                               

Apply the advice throwing to catch any exception that occur in the RunCommand method, and change the result of the method.                                                               
     
using System;

namespace ExceptionSample
{
        public aspect CatchAspect
        {
                pointcut MyPointCut bool %.RunCommand(int i);

                throwing bool MyPointCut(Exception ex)(int i)
                {
                        Console.WriteLine("Error: " + ex.Message);
                        return false;
                }
        }

        public class MainCls
        {
                public bool RunCommand(int i)
                {
                        int k = 10 / i;
                        Console.WriteLine("Division: " + k);
                        return true;
                }

                public static void Main()
                {
                        MainCls mainCs = new MainCls();
                        bool success;

                        for (int i = 0; i < 3; i++)
                        {
                                Console.WriteLine("Running command: " + i);
                                success = mainCs.RunCommand(i);
                                Console.WriteLine((success? "OK": "Not OK"));
                        }
                }
        }
}                                                                                                                                       
                                    Program output:
                                   



  • Constructor.cs                                                               
                                                                    Apply the advice after and before in a constructor.


    using System;

    namespace ConsoleApplication1
    {
            public aspect Class2
            {
                    pointcut MyPointCut ctor ConsoleApplication1.MainCls.MainCls();

                    after MyPointCut()
                    {
                            Console.WriteLine("after " + thisJoinPoint.MethodInfo);
                    }

                    before MyPointCut()
                    {
                            Console.WriteLine("before " + thisJoinPoint.MethodInfo);
                    }
            }

            public class MainCls
            {
                    public MainCls()
                    {
                            Console.WriteLine("MainCls()");
                    }


                    public static void Main()
                    {
                            new MainCls();
                    }
            }
    }
                                                                                                                                           
                                    Program output:
                                   
  • around3.cs
    Use the around advice to cache the result of all method that return a boolean                                                                value. So, if you can call the method twice (wth the same parameters), the                                                                second time, the result is read from cache.                                                               
         
    using System;
    using System.Collections;

    namespace AroundSample
    {
            public aspect AroundAspect
            {
                    pointcut IsMethodsCache bool MainCls.Is%(...);

                    Hashtable cache = new Hashtable();

                    around bool IsMethodsCache()
                    {
                            bool result;

                            // makes a unique key
                            string key = thisJoinPoint.MethodInfo.GetHashCode().ToString();
                            foreach (object param in thisJoinPoint.ParamsData)
                                    key += "." + param.GetHashCode();

                            if (cache[key] == null)
                            {
                                    //Console.WriteLine("No cache is avaiable");
                                    result = proceed(...);
                                    cache[key] = result;
                            }
                            else
                            {
                                    Console.WriteLine("Get result from cache");
                                    result = (bool)cache[key];
                            }
                            return result;
                    }
            }

            public class MainCls
            {
                    public bool IsPrime(long number)
                    {
                            for (long d = 2; d < number; d++)
                            {
                                    if (number % d == 0)
                                    {
                                            return false;
                                    }
                            }

                            return true;
                    }

                    public bool IsSumBiggerThanZero(int a, int b)
                    {
                            return (a + b > 0);
                    }

                    public int IsOtherThing()
                    {
                            return 0;
                    }

                    public static void Main()
                    {
                            MainCls mainCs = new MainCls();
                            Console.WriteLine("7  is prime = " + mainCs.IsPrime(7));
                            Console.WriteLine("4  is prime = " + mainCs.IsPrime(4));
                            Console.WriteLine("7  is prime = " + mainCs.IsPrime(7));

                            Console.WriteLine("4+-1 > 0 = " + mainCs.IsSumBiggerThanZero(4,-1));
                            Console.WriteLine("7+32 > 0 = " + mainCs.IsSumBiggerThanZero(7,32));
                            Console.WriteLine("4+-1 > 0 = " + mainCs.IsSumBiggerThanZero(4,-1));
                    }
            }
    }
                                                                                                                                           
                                    Program output:
                                   


DownloadAlpha 0.1.3
                                                                                                                                                                                               
NKalore-Comp-0.1.3-bin.zip
NKalore-Comp-0.1.3-src.zip
NKalore-Comp-0.1.3-samples.zip






Using AOP in C#




IntroductionToday, AOP is starting to get attention in the .NET community, andright now, there are a lot of frameworks and compilers for .NET toallow 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 theuse 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 NKaloreTo 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-linemode. At the moment, integration with Visual Studio is not available.It is possible to have a basic integration with #Develop, but it’s asubject for another article.
Creating your first AOP applicationIn 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:
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:
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), butwe 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:

Logging everything in your code with BEFORE and AFTER advicesThe 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:
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 consolewith information that is in the "thisJointPoint" variable. See the aspect implemented below:
Collapse
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.ParameterType, pis.Name,
                          joinPoint.ParamsData);
            }
        }
      }

      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:

The AROUND adviceAnother very interesting advice is the AROUND advice.This advice is executed during the execution of the method. So, inreality, when you use this advice, when the method "captured" by thepointcut is executed, your aspect code is executed, and to execute thereal 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:
Collapse
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 AROUNDadvice. First, we log into the console the method that is beingexecuted, writing "before..." and "after...", when the call to theoriginal 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 oursamples), it will write in the console. The output of this program is:

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.
ConclusionNKalore offers a very simple way to implement AOP into C#, and it’svery intuitive. The project is still in the Alpha stage, but for awhile, we can make little programs with it. As roadmap of NKalore says,in the future, probably we will have some more AOP features andintegration with Visual Studio, which would be very nice.


开源项目首页:http://aspectsharpcomp.sourceforge.net/
最后编辑OpenSource 最后编辑于 2008-04-28 17:56:49