Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Aspect Oriented Programming: learn step by step and roll your own implementation!

0.00/5 (No votes)
11 Sep 2013 6  
A journey into AOP land with concerns, pointcuts, joinpoints, advices, aspects, interceptors, proxies, targets, mix-ins, composites...
This article started with my need for a good and simple interception mechanism for some requirements that could be solved using AOP techniques. There are plenty of interceptors out there but most of them (Castle.net, Spring.net, LinFu, etc..) would require to emit dynamic child classes through IL code at runtime and thus ends with almost always the same limitations regarding the classes you can intercept : they can not be static, must be non-sealed, properties and methods must be virtual, etc...
Other interceptions mechanism would require to change the build process or to buy a license. I could not afford one or the other...

Table of content

Introduction

AOP stands for Aspect Oriented Programming. I guess every reader is familiar with the OP part of the acronym so we will have to clarify what Aspect means and don’t worry we’ll come to that later in the article.

I’ll (try to) keep this article at beginner level. Knowledge of Object Oriented Programming concepts is the only requirement to read further!

In my opinion understanding properly a concept make you a better consumer of its implementation. But, I do understand that the article is a bit long so if you feel bored or discouraged I still encourage you to jump to the implementation part. You'll still be able to come back to theory later.

Advanced developers, read this!

If you are familiar with AOP then don't leave yet!
Let me already disclose straight away what this article has to offer...

I will introduce an interception technique which allows to intercept:

  • any class (including sealed, static, value types)
  • constructors
  • type initializers
  • instance methods, properties and eventsand events (even if they are not marked as virtual)
  • static methods, properties and events

Without

  • Weaving your code or your assemblies
  • emitting IL code
  • using any dynamic stuff
  • modifying your target class
  • forcing the weavers to implement anything (such as MarshalByRef)

Basically we are talking about a pure managed code technique which could run on .Net 1.1 (actually I use bits of Linq but you can change t that easily) and which allows you to intercept almost anything you might think of.

Let's be more clear. With the following technique:

you can intercept stuff such as System.DateTime.Now or System.IO.File operations!
you don't have the usual limitations of the most popular interceptions library.

Are you guys doubting ? then read further!!

The principles of AOP

Background

Some might think they have not made their full journey into Object Oriented Programming so why would they switch from OOP to AOP and abandoned all the concepts they hardly have learnt over the years? Answer is simple: there is no switch. There is no: OOP vs AOP! AOP is one of these concepts which name, in my opinion, is misleading. Embracing the AOP principles lets you dealing with classes, objects, interfaces, inheritance, polymorphism, abstraction etc… so there is no way you get lost as you are still fully immersed in the OOP world.

When applying AOP concepts in your code you are attempting to relax one particular, and not the least, principle of OOP which is encapsulation to adress cross-cutting concerns (we'll come back to that later on).

Back in the old days, when internet was only yellow pages, bulleting boards and usenet, you were better to read books if you wanted to learn anything (comment for the y generation : a book is a thing with written sheets of paper stuffed in it). And all these books would approximately remind you this regarding the OOP subject:

  • Rule #1: you have to encapsulate all you data and code
  • Rule #2: never ever break Rule #1

Encapsulation has been the ultimate goal for introducing OOP concepts in 3rd generation languages (3GL).

Wikipedia:
Encapsulation is used to refer to one of two related but distinct notions, and sometimes to their combination:
  • A language mechanism for restricting access to some of the object’s components.
  • A language construct that facilitates the bundling of data with the methods (or other functions) operating on that data.

AOP, in a way, is claiming that it, sometimes, should be possible to use:
a language construct that facilitates the bundling of methods (or other functions) operating with encapsulated data without the data.

Got that? Then you get all that is to know about the theory. Good job!

Now we are naturally leaded to the 2 following questions:

  • When is “sometimes”?
  • Why should it be possible?

About usefulness of AOP... some scenarii

Let's take a look at a the following situations:

Scenario A

You are software developer in a bank. The bank has a pretty working operational system. The business is running smoothly. Government emits a policy which enforces banks to commit on some sort of transparency. Whenever money goes in or out of the bank it should be logged. The government publicly said that this is a first measure towards transparency but that more is to be expected.

Scenario B

Your web application has been released to test team. All functional tests passed but the application failed at load testing. A non-functional requirement stated that no page should take more than 500 ms to process on the server. After analysis there are dozens of queries made to the database that could be avoided by caching the results.

Scenario c

You have spent the last 2 years modeling your domain model in a perfect library consisting of 200+ classes. Lately you’ve been told that a new application front-end will be written and this guy needs to bind your objects to the UI. But to facilitate that task all your classes should now implement INotifyPropertyChanged.

These examples are valid in terms of the why and when AOP could come to the rescue. These scenarii have the following in common:

Cross-cutting concerns

When is "sometimes”?

Some classes (Bank class, Data access services, Domain model classes, etc…) designed to achieve a given functionality have to be modified to handle a requirement which is basically “not their own business”.

  1. The Bank class purpose is money exchange. The logging concern is Government’s interest.
  2. The Data Services classes purpose is to retrieve data. The data caching concern is a non-functional requirement.
  3. The domain model classes purpose is to handle your company’s business. The concern about notification of property changed is in the UI interest.

It is whenever you have to write some code over different classes to fulfill an interest external to these classes. In AOP dialect it is whenever you have a cross-cutting concern.

The notion of cross-cutting concern is centric to AOP. No cross-cutting concern = no need for AOP!

why should it be possible?

Let’s take a closer look at Scenario C.
Your problem is that you have an average of 5 properties exposed by each class in your domain model. With 200+ classes you will have to implement (copy/paste) more than 1,000 times some boiler plate code to transform something which looks like this:

public class Customer
{
    public string Name { get; set; }
}

Into something like that:

public class Customer : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
        
    private string _Name;
    public string Name 
    { 
        get { return _Name; }
        set
        {
            if (value != _Name)
            {
                _Name = value;
                SignalPropertyChanged("Name");
            }
        }
    }

    void SignalPropertyChanged(string propertyName)
    {
        var pcEvent = this.PropertyChanged;
        if (pcEvent != null) pcEvent(this, new PropertyChangedEventArgs(propertyName));
    }
}

Uh! And that is for one property only! BTW, did you know the intern left 2 days ago?

You surely get the why without further explanations

It should be possible "to bundle the methods operating with encapsulated data without the data" in other words : externalise the implementation of the cross-cutting INotifyPropertyChanged concern with no or minimum impact on domain model classes like Customer.

If we can achieve that then we will:

  1. Have a proper separation of concerns
  2. Avoid code repetition and thus facilitate code maintenance
  3. Avoid hiding domain classes core business under tons of boiler plate code

Yeahh... ok, that's all nice and fancy and stuff but how can we do?

The theorical answer

We have a cross-cutting concern which requires some code to be executed in several classes (targets from now on).
The implementation (the code which implements Logging, or Cache, or whatever) is simply called concern in the AOP world.

We should then be able to attach (inject, introduce, etc... choose your word) our concern (I repeat because it is important: the concern is the implementation for your cross-cutting concern) at any chosen place of the target.
And we should be able to choose any of the following places of the target to attach our concern:

  • Static initializers
  • Constructors
  • Static Property getters and setters
  • Instance Property getters and setters
  • Static methods
  • Instance Methods
  • Destructors

In a perfect AOP world we should be able to attach our concern at any line of code of the target.

Fine, but if we want to attach a concern we need a hook in the target, don’t we? Yes captain!
In AOP the notion of that hook (the place where your concern is going to be attached for execution) has a name: it is a pointcut. And the place from where you actualy attach the code has also a name : it is a joinpoint.

Clear enough? Maybe not.... Here is some pseudo-code that hopefully demonstate the idea:

// Target class
class BankAccount
{
    public string AccountNumber {get;}
    public int Balance {get; set;}
    void Withdraw(int AmountToWithdraw)
    {
       
:public pointcut1;   // a pointcut (imagine something like a label for goto statement in BASIC or T-SQL etc...)
        Balance -= AmountToWithdraw;
    }
}

// Concern 
concern LoggingConcern
{
    void LogWithdraw(int AmountToWithdraw)
    { 
        // Here you have to imagine that some kind of 'magic' has happened
        // and 'this' is an instance of the BankAccount class.
        Console.WriteLine(this.AccountNumber + " withdrawal on-going...");
    }
}

class Program
{
    void Main()
    {
        // get a reference to the pointcut marker  through reflection
        pointcut = typeof(Bank).GetPointcut("pointcut1");

        // this is the joinpoint 
        LoggingConcern.Join(cutpoint,
    LogWithdraw); 

        // After joinpoint the runtime should have (in a sort of registry)
        // a record which tells to execute our LoggingConcern at pointcut1 of Bank class
    }
}

Would'nt it be great to have such mechanism available out of the C# box???

A few more concepts

Before we move on to our actual implementation let's introduce a few more definitions...

What is an Aspect?
It is the association of a concern, a point cut and a joinpoint.
Think of it for a second and I hope it will be crystal clear: the fact that I have a Logging mechanism (concern), that I register its log method to be executed (joinpoint) at a given place of my application code (pointcut) is one aspect of my application.

But wait a minute... What could/should a concern be allowed to do once injected?

Concerns are categorized into 2 categories:

  • Side effects:
    A side effect is a concern which is not changing the behavior of the code at pointcut. A side effect just introduce an additional action to perform.
    A Logging concern is a good example of a side effect: whenever the runtime execute the targeted method (eg:Bank.Withdraw(int Amount)) the LoggingConcern.LogWithdraw(int Amount) method will be executed and the Bank.Withdraw(int Amount) method will continue execution.

  • Advices:
    An advice is a concern which might change the input/output of the method.
    A Caching concern is a perfect example: Whenever the runtime execute the targeted method (eg: CustomerService.GetById(int Id)) the CachingConcern.TryGetCustomerById(int Id) will be executed and will force the CustomerService.GetById(int Id) method to return with the value found in cache, if any, otherwise will let the execution continue.

    Advices should be allowed to:
    • Inspect the input parameters at targeted pointcut and modify them if needed
    • Cancel the execution of the targeted method and replace it with a different implementation
    • Inspect the output result od the targeted method and modify or replace it

At that point if you are still reading then congratulations! Bravo! Parce que vous le valez bien...

We are done with the general concepts and ideas of AOP. Let's move forward and see how we can get close to that with C#...

Our implementation

Show me some code ou je tue le chien!

Concerns

The concern should have a magic this behavior which is of our target type.
That's no problemo!

public interface IConcern<T>
{
    T This { get; } // ok, that's a bit of cheating but who cares?
}

Pointcuts Reference

There is no easy way to get pointcuts for every single line of code. But we can get one at each method call and that's fairly easy by using the System.Reflection.MethodBase class. MSDN is not really verbose about it: Provides information about methods and constructors.[sic].

Between you and me using MethodBase for getting reference to pointcuts is the most powerful possibility at your disposition.

You can get reference to pointcuts for Constructors, Methods, Properties and Events as for .Net almost anything you declare in your code (apart from fields) ends up being a method...
See yourselves:

            
public class Customer 
{
    public event EventHandler<EventArgs> NameChanged;
    public string Name { get; private set; }
        
    public void ChangeName(string newName) 
    {
        Name = newName;
        NameChanged(this, EventArgs.Empty);
    }
}

class Program
{
    static void Main(string[] args)
    {
        var t = typeof(Customer);

        // Constructor (not limited to parameterless)
        var pointcut1 = t.GetConstructor(new Type[] { });

        // The ChangeName method
        var pointcut2 = t.GetMethod("ChangeName");

        // the Name property
        var nameProperty = t.GetProperty("Name");
        var pointcut3 = nameProperty.GetGetMethod();
        var pointcut4 = nameProperty.GetSetMethod();

        // Everything about the NameChanged event
        var NameChangedEvent = t.GetEvent("NameChanged");
        var pointcut5 = NameChangedEvent.GetRaiseMethod();
        var pointcut6 = NameChangedEvent.GetAddMethod();
        var pointcut7 = NameChangedEvent.GetRemoveMethod();
    }
}

Joinpoints

Writing the code for joining is fairly easy as well. Look at the signature of the method below :

void Join(System.Reflection.MethodBase pointcutMethod, System.Reflection.MethodBase concernMethod);

We can add that signature to a sort of registry that we will provide later on and we can already imagine writing code like this one!!!

public class Customer
{
    public string Name { get; set;}
    public void DoYourOwnBusiness() 
    {
        System.Diagnostics.Trace.WriteLine(Name + " is doing is own business");
    }
}

public class LoggingConcern : IConcern<Customer>
{
    public Customer This { get; set; }

    public void DoSomething() 
    {
        System.Diagnostics.Trace.WriteLine(This.Name + " is going to do is own business");

        This.DoYourOwnBusiness();
            
        System.Diagnostics.Trace.WriteLine(This.Name + " has finished doing its own business");
    }
}

class Program
{
    static void Main(string[] args)h
    {
        // Get a pointcut for Customer.DoSomething();
        var pointcut1 = typeof(Customer).GetMethod("DoSomething");
        var concernMethod = typeof(LoggingConcern).GetMethod("DoSomething");

        // Join them
        AOP.Registry.Join(pointcut1, concernMethod);
    }
}

How far are we from our pseudo-code? Personally I would say not much...
What's next then?

Glueing everything together...

That's where problems and fun start at the same time!

But let's start simple with

The registry

The registry will keep records about joinpoints. It's a singleton list of joinpoint items.
A joinpoint is a simple struct :

public struct Joinpoint
{
    internal MethodBase PointcutMethod;
    internal MethodBase ConcernMethod;
    
    private Joinpoint(MethodBase pointcutMethod, MethodBase concernMethod)
    {
        PointcutMethod = pointcutMethod;
        ConcernMethod = concernMethod;
    }

    // Utility method to create joinpoints 
    public static Joinpoint Create(MethodBase pointcutMethod, MethodBase concernMethod)
    {
        return new Joinpoint (pointcutMethod, concernMethod);
    }
}

Nothing fancy... It should as well implement IEquatable<Joinpoint> but for making the code shorter here I have intentionally removed it

And the registry: The class is called AOP and implements the singleton pattern. It exposes its unique instance through a public static property named Registry

public class AOP : List<Joinpoint>
{
    static readonly AOP _registry;
    static AOP() { _registry = new AOP(); }
    private AOP() { }
    public static AOP Registry { get { return _registry; } }

    [MethodImpl(MethodImplOptions.Synchronized)]
    public void Join(MethodBase pointcutMethod, MethodBase concernMethod)
    {
        var joinPoint = Joinpoint.Create(pointcutMethod, concernMethod);
        if (!this.Contains(joinPoint)) this.Add(joinPoint);
    }
}

With the AOP class we can now write construct like :

AOP.Registry.Join(pointcut, concernMethod);

Houston, we've got a problem

We've got an obvious and serious problem now to cope with. If a developer writes code like...

var customer = new Customer {Name="test"};
customer.DoYourOwnBusiness();

... there is just no reason why our registry would be consulted, so there is no way that our LoggingConcern.DoSomething() method is executed...

Our problem is that .Net does not provide us with a simple way to intercept such calls out of the box.

As there is no native way then some work around must be implemented. The capabilities of your work around is going to drive the capabilities of your AOP implementation.
The goal of this aricle is not to discuss all possible interception techniques but take note that the interception model is the key differentiator between all AOP implementations.
The SharpCrafters website (owners of PostSharp) is providing some clear information on the 2 major techniques:

Our interception technique: Proxying

There is not much of a secret if you want to intercept all calls made to a class you have 3 choices:

  1. Create your own language and compiler to produce .net assemblies: when compiling you can inject whatever you want everywhere you want.
  2. Implement a solution which modifies the runtime behavior of assemblies
  3. Give a proxy to your consumer and intercept calls using an Interceptor class while marshaling the real subject (target)

For advanced guys: I voluntarily don't mention Debugger API and Profiler API possibilities which are not viable for production scenarii.
For very advanced one: An hybrid of solutions 1 and 2 using the Roslyn API should be feasible and, as far as I know, it is still to be invented. A bon entendeur...

Apart if you need to provide pointcuts at any single line of code then it seems that the 2 first solutions are a bit of over-engineering.
We'll go for the 3rd solution. Take note that usage of proxying technique comes with a good and a bad news:

The bad news is that your target object must be swapped at runtime with a proxy object instance. Implying that if you want to intercept things such as constructors you'll have to delegate construction of your target class instances to a factory (that's a cross-cutting concern that this implementation won't solve. If you already have an instance of the target class then you will have to explicitely ask for the swap to happen. For the IOC and Dependency Injection ninjas the delegation of objects creation will be less than an issue. For others it means they'll have to use a factory if they want to use our interception technique at its full extent. But don't worry we are going to implement that factory.

The good news is that we have nothing to do to implement a proxy. The class System.Runtime.Remoting.Proxies.RealProxy will build it for us in a highly optimized way.

In my opinion the class name does not reflect its use. This class is not a Proxy it is an Interceptor. But anyway that class will provide us with a Proxy by calling its method GetTransparentProxy() and that's the only thing we need.

So the skeleton for our interceptor is :

public class Interceptor : RealProxy, IRemotingTypeInfo
{
    object theTarget { get; set; }

    public Interceptor(object target) : base(typeof(MarshalByRefObject))
    {
        theTarget = target;
    }

    public override System.Runtime.Remoting.Messaging.IMessage Invoke(System.Runtime.Remoting.Messaging.IMessage msg)
    {
        IMethodCallMessage methodMessage = (IMethodCallMessage) msg;
        MethodBase method = methodMessage.MethodBase;
        object[] arguments = methodMessage.Args;

        object returnValue = null;

        // TODO:
        // here goes the implementation details for method swapping in case the AOP.Registry 
        // has an existing joinpoint for the MethodBase which is hold in the "method" variable...
        // if the Registry has no joinpoint then simply search for the corresponding method 
        // on the "theTarget" object and simply invoke it... ;-)

        return new ReturnMessage(returnValue, methodMessage.Args, methodMessage.ArgCount, methodMessage.LogicalCallContext, methodMessage);
    }

    #region IRemotingTypeInfo
    public string TypeName { get; set; }
    public bool CanCastTo(Type fromType, object o) { return true; }
    #endregion
}

Some explanations are required here as we are now touching the heart of the implementation....

The RealProxy class exists to serve the purpose of intercepting calls from remote objects and marshal a targeted object. By remote here you must understand really remote like : objects living in another application, another AppDomain, another server, etc...). I am not going to go too much in details but there were 2 ways to marshal objects in the .net Remoting infrastructure : by reference or by value. Basically it means you can only marshal remote objects if they are inheriting MarshalByRef or if they implement ISerializable. Our plan is not to use the remoting capabilities at all but we still need to let the RealProxy class think our target is acceptable for remoting. That's why we pass typeof(MarshalByRef) to the RealProxy base constructor.

The RealProxy class is receiving all calls made on the transparent proxy via the System.Runtime.Remoting.Messaging.IMessage Invoke(System.Runtime.Remoting.Messaging.IMessage msg) method. That's where we will implement the details about method swapping. Read the comments in the code above.

About the implementation of IRemotingTypeInfo: In a true remoting environment the client side would request an object to the server. The client application runtime might not know anything about the type of the marshalled remote object. So when the client app makes a call to the method public object GetTransparentProxy() the runtime must decide if the returned object (the transparent proxy) is boxable to the client application expected contract. By implementing IRemotingTypeInfo you give a hint to the client runtime telling if casting to a specified type is allowed or not.
And guess what the trick is there, in front of your astonished gaze, right here...

public bool CanCastTo(Type fromType, object o) { return true; }    

All our AOP implementation is only possible due to the possibility offered by remoting to write these 2 words: return true; Passed that point we can cast the object returned by GetTransparentProxy() to whatever interface without any runtime check!!!.

The runtime just purely and simply gave us a "yes card" to play with!

We might want to revisit that code to return something more appropriate than true to any type... But we could also imagine make use of this behaviour to provide a Missing Method implementation or a catch all interface... There is a lot of room fot your creativity to express istself here!

At that point we have a decent interception mechanism for our target instance. We are still mising interception of constructors and creation of the transparent proxy. That's a job for a factory...

The Factory

Not much to say about that one. Here is the skeleton of the class.

public static class Factory
{
    public static object Create<T>(params object[] constructorArgs)
    {
        T target;

        // TODO:
        // Base on typeof(T) and the list of constructorArgs (count and their Type)
        // we can ask our Registry if it has a constructor method joinpoint and invoke it
        // if the Registry has no constructor joinpoint then simply search for the corresponding one 
        // on typeof(T) and invoke it...
            

        // Assign the result of construction to the "target" variable
        // and pass it to the GetProxy method.
        return GetProxyFor<T>(target);
    }

    public static object GetProxyFor<T>(object target = null)
    {
        // Here we are asked to intercept calls on an existing object instance (Maybe we constructed it but not necessarily)
        // Simply create the interceptor and return the transparent proxy
        return new Interceptor(target).GetTransparentProxy();
    }
}
        
        

Note that the Factory class is always returning an object of type object. We can't return an object of type T because the transparent proxy is simply not of type T, it is of type System.Runtime.Remoting.Proxies.__TransparentProxy. But, remember the "Yes card", we can cast the returned object to whatever interface without any runtime checking!

We will nest the Factory class in the AOP class hoping to give a neat programming experience to our consumers. But you'll see that in the Usage section below

Final notes on implementation

If you have read the whole article till that point I must recognize you are almost a hero! Bravissimo! Kudos!

For the sake of brevity and clarity of this article (damned... why are you smiling?) I am not going to discuss the boring implementation details of method retrievals and switching. There is actually not much fun in it. But if you are interested in that piece then you can download the code and browse it : it is fully functional!. The classes and methods signature might be a bit different as I am coding while but no major change is to be expected.

Warning: Before deciding to use this code in your project please read carefully the paenultimus section. And if you don't know the word paenultimus then I guess you have to click the link first!

Usage

I have been writing a lot but did not give you, yet, a proper hint on how we can actually use all of this. And finally here we are : the moment of truth!

What you get when downloading

The attached zip file includes a project with 5 examples for the sake of demonstration. So you will get examples of injecting aspects by:

  • Intercepting a constructor
  • Intercepting methods and properties
  • Intercepting events
  • Intercepting type initialization
  • Intercepting File.ReadAllText(string path)

Now I am going to show two out of these five: the most and the less obvious

Intercepting methods and properties

First we need a domain model... Nothing fancy

            
public interface IActor
{
    string Name { get; set; }
    void Act();
}

public class Actor : IActor
{
    public string Name { get; set; }

    public void Act()
    {
        Console.WriteLine("My name is '{0}'. I am such a good actor!", Name);
    }
}

    

Then we need a concern

public class TheConcern : IConcern<Actor>
{
    public Actor This { get; set; }

    public string Name 
    {
        set
        {
            This.Name = value + ". Hi, " + value + " you've been hacked";
        }
    }

    public void Act()
    {
        This.Act();
        Console.WriteLine("You think so...!");
    }
}
    

At application initialization we tell the Registry about our joinpoints

// Weave the Name property setter
AOP.Registry.Join
    (
        typeof(Actor).GetProperty("Name").GetSetMethod(),
        typeof(TheConcern).GetProperty("Name").GetSetMethod()
    );

// Weave the Act method
AOP.Registry.Join
    (
        typeof(Actor).GetMethod("Act"),
        typeof(TheConcern).GetMethod("Act")
    );    
    
    

And finally we create an object via the Factory

var actor1 = (IActor) AOP.Factory.Create<Actor>();
actor1.Name = "the Dude";
actor1.Act();    

Note that we requested the creation of an Actor class but we can cast the result to an interface so let's use IActor as the class is implementing it.

If you run that in a Console application the output will be the following:

    
            
My name is 'the Dude. Hi, the Dude you've been hacked'. I am such a good actor!
You think so...!    

Intercepting File.ReadAllText(string path)

Here we have 2 slight issues:

  1. the File class is static
  2. and does not implement any interface

That's where we benefit from the "Yes card"! Remember? There is no runtime type checking between the returned proxy and the interface.
Which means we can create any kind of interface... no one as to implement it anyway: neither the target nor the concern. Basically we are only using the interface as a contract...

Let's demonstrate that by creating a fake interface to mimic the static File class

public interface IFile
{
    string[] ReadAllLines(string path);
}

Our concern

public class TheConcern
{
    public static string[] ReadAllLines(string path)
    {
        return File.ReadAllLines(path).Select(x => x + " hacked...").ToArray();
    }
}

The registering of joinpoints

AOP.Registry.Join
    (
        typeof(File).GetMethods().Where(x => x.Name == "ReadAllLines" && x.GetParameters().Count() == 1).First(),
        typeof(TheConcern).GetMethod("ReadAllLines")
    );

And finally execution of the program

var path = Path.Combine(Environment.CurrentDirectory, "Examples", "data.txt");
var file = (IFile) AOP.Factory.Create(typeof(File));
foreach (string s in file.ReadAllLines(path)) Console.WriteLine(s);

In this case please note that we can not use the Factory.Create<T> method as static types cannot be used as generic arguments.

References

In no particular order:

Where to go from there? 

So far we have been able to achieve the primary goal of AOP : implement an aspect and register it for execution. TinyAOP is born. But your journey in AOP land is not finished yet and you might want to dig further: 

  • Reason #1: Who wants to register Joinpoints the way we are now? Not me, for sure! With a bit of introspection we can make things much more practical and build something that look like a real AOP library. AOP is there to facilitate your life not to introduce more pain. 
  • Reason #2: Matters such as mix-ins and composition are completely uncovered and we can expect tons of goodness there. 
  • Reason #3: We need performance and stability. Now the code is just a proof of concept. It is far too slow when we can make it very fast. A bit of error checking would not be bad either.
  • Reason #4: We are intercepting almost any kind of class but what about interfaces interception??
  • Reason #5: Do we really need more reasons ?

Conclusion: We have a nice and tiny prototype which demonstrates the technical feasibility of doing AOP purely with managed, non-dynamic, code without weaving, etc...

You have learnt AOP : you can start from here and roll your own implementation! 

A few last words, off-topic...

About french expressions 

No secret that I am french... Nobody's perfect! While writing this article I was googling for a place where the expression "A boire, ou je tue le chien!" would be explained and I found that page called "French expressions you won't learn at school". I am sure you might find some of these expressions pretty funny so I am sharing the link : http://www.globule.org/~gpierre/french.php

Claim

A website such as Codeproject is only working because some guys are writing and publishing articles. Whatever the reasons why these guys are doing it, they are! And that takes a non-negligible amount of work and time.

Please do not neglect that time and work:
If you don't like the article please refrain to give your vote of 1 without further explanations... I might have wrote a statement which is wrong or false, my english surely needs rephrasing, maybe you are expecting more or less explanations, I don't know... It is as simple as that: I don't know if you don't tell! Your justified bad ratings are welcome I am not gonna hurt (ok, maybe a bit ) and it will allow me to revise my judgment, make any necessary adjustments or corrections to the article and also to improve myself for future ones.

Now if you liked the article, or you are using the code or if you have learned something today then let me know as well : leave a comment, give me your vote (of 5), drop me an email, connect on LinkedIn... Whatever form of feedback is much appreciated!!!

Thanks for reading!

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