Click here to Skip to main content
15,892,298 members
Articles / Programming Languages / C#

Dynamic Decorator, Unity and Castle DynamicProxy Comparison

Rate me:
Please Sign up or sign in to vote.
4.50/5 (3 votes)
26 Jul 2011CPOL4 min read 35.3K   329   13   6
Compares performance and features of Dynamic Decorator, Unity and Castle DynamicProxy for extending functionality of object

Introduction

Extending functionality by attaching behaviors to object has advantages over extending functionality by modifying existing classes or creating new classes. However, its adoption is still limited. The reason, I think, is that either the tools available make it too complex or lack of features to do serious programming based on them. The situations are changing now. With new tools that are coming out with more features and are still simple to use, developers should seriously start to think of extending functionality by attaching behaviors to object instead of extending functionality by modifying existing classes or creating new classes.

There are three tools that can be used to extend functionality of object in the .NET world. They are Castle DynamicProxy, Unity and Dynamic Decorator. This article discusses their performance and features in extending functionality of objects.

Scope

The CreateProxy method of the ObjectProxyFactory class in the Dynamic Decorator is mostly close to CreateInterfaceProxyWithTarget method of the ProxyGenerator class in the Castle DynamicProxy and ThroughProxy method of the Intercept in the Unity. Therefore, the discussion is limited to use them to add some preprocessing/postprocessing functionality to an existing object that implements an interface.

Test Code

The test code is listed as follows.

C#
//EnterLogBehavior class for Unity
public class EnterLogBehavior : IInterceptionBehavior
{
    public IEnumerable<Type> GetRequiredInterfaces()
    {
        return Type.EmptyTypes;
    }

    public bool WillExecute
    {
        get { return true; }
    }

    public IMethodReturn Invoke(IMethodInvocation input, 
		GetNextInterceptionBehaviorDelegate getNext)
    {
        Console.Write("Calling: " + input.MethodBase.Name + "\n");
        var methodReturn = getNext().Invoke(input, getNext);
        return methodReturn;
    }
}

//ExitLogBehavior class for Unity
public class ExitLogBehavior : IInterceptionBehavior
{
    public IEnumerable<Type> GetRequiredInterfaces()
    {
        return Type.EmptyTypes;
    }

    public bool WillExecute
    {
        get { return true; }
    }

    public IMethodReturn Invoke(IMethodInvocation input, 
		GetNextInterceptionBehaviorDelegate getNext)
    {
        var methodReturn = getNext().Invoke(input, getNext);
        Console.Write("Exiting: " + input.MethodBase.Name + "\n");
        return methodReturn;
    }
}

//LoggingInterceptor class for Castle DynamicProxy
public class LoggingInterceptor : Castle.DynamicProxy.IInterceptor
{
    private int m_count;
    public void Intercept(IInvocation invocation)
    {

        Console.Write("Calling: " + invocation.Method.Name + "\n");
        invocation.Proceed();
        Console.Write("Exiting: " + invocation.Method.Name + "\n");
    }

}
    
class Program
{
    static void Main(string[] args)
    {
        IEmployee emp = new Employee(1, "John", "Smith", new DateTime(1990, 4, 1), 1);
        System.Int32? id = null;
        System.String detail = "";
        IEmployee iemp;
        TimeSpan? ts = null;

        System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();

        //Performance test for Unity
        stopWatch.Start();
        iemp = Intercept.ThroughProxy<IEmployee>(emp, new InterfaceInterceptor(),
            new[] { (IInterceptionBehavior)new EnterLogBehavior(), 
		(IInterceptionBehavior)new ExitLogBehavior() });

        id = iemp.EmployeeID;
        detail = iemp.DetailsByLevel(2);
        stopWatch.Stop();
        ts = stopWatch.Elapsed;
        Console.Write("Using Unity ThroughProxy: " + ts.Value.ToString() + "\n\n");

        //Performance test for Castle DynamicProxy
        stopWatch.Restart();
        ProxyGenerator generator = new ProxyGenerator();
        iemp = generator.CreateInterfaceProxyWithTarget<IEmployee>
			(emp, new LoggingInterceptor());

        id = iemp.EmployeeID;
        detail = iemp.DetailsByLevel(2);
        stopWatch.Stop();
        ts = stopWatch.Elapsed;
        Console.Write("Using Castle CreateInterfaceProxyWithTarget: " 
			+ ts.Value.ToString() + "\n\n");

        //Performance test for Dynamic Decorator
        stopWatch.Restart();
        iemp = ObjectProxyFactory.CreateProxy<IEmployee>(
            emp,
            new String[] { "DetailsByLevel", "get_EmployeeID" },
            new Decoration((x, y) =>
            {
                Console.Write("Calling: " + x.CallCtx.MethodName + "\n");
            }, null),
            new Decoration((x, y) =>
            {
                Console.Write("Exiting: " + x.CallCtx.MethodName + "\n");
            }, null));

        id = iemp.EmployeeID;
        detail = iemp.DetailsByLevel(2);
        stopWatch.Stop();
        ts = stopWatch.Elapsed;
        Console.Write("Using Dynamic Decorator: " + ts.Value.ToString() + "\n\n");


        //More features for Dynamic Decorator
        emp = ObjectProxyFactory.CreateProxy<IEmployee>(
            emp,
            new String[] { "DetailsByLevel" },
            new Decoration((x, y) =>
            {
                IMethodCallMessage method = x.CallCtx;
                string str = "Calling " + x.Target.GetType().ToString() + "." 
				+ method.MethodName +
                    "(";
                int i = 0;
                foreach (object o in method.Args)
                {
                    if (i > 0)
                        str = str + ", ";
                    str = str + o.ToString();
                }
                str = str + ")";

                Console.WriteLine(str);
                Console.Out.Flush();
            }, null),
            null);

        id = emp.DepartmentID;
        detail = emp.DetailsByLevel(2);
    }
}

In the above code, an object emp of class Employee is created. First, the ThroughProxy method of the Intercept class of the Unity is used to add entering/exiting logs to object emp. Second, the CreateInterfaceProxyWithTarget method of the ProxyGenerator of Castle DynamicProxy is used to add entering/exiting logs to object emp. Third, the CreateProxy method of the ObjectProxyFactory class of the Dynamic Decorator is used to add entering/exiting logs to object emp.

During the execution of the above code, you will see the following output:

Image 1

First, let's take a look at the performance of each tool. For doing exactly the same thing, the Unity took 111.3ms, the Castle DynamicProxy took 248.3ms while the Dynamic Decorator took 3.9ms. Though all of them declare that they are lightweight tools, you can see which one is truly lightweight in adding extra behaviors to object. The Dynamic Decorator is clearly a winner in terms of performance.

Second, let's see how the behaviors are defined for each of them. For Unity, each of the behaviors is a class implementing interface IInterceptionBehavior, e.g., EnterLogBehavior and ExitLogBehavior. For Castle DynamicProxy, the behaviors are in one single class LoggingInterceptor implementing interface Castle.DynamicProxy.IInterceptor. For Dynamic Decorator, each of the behaviors is a method (the lambda expressions for this example). Therefore, conceptually, the Dynamic Decorator is simpler.

Next, let's look into more features available from the Dynamic Decorator. For Unity and Castle DynamicProxy, the behaviors are attached to all methods of the object. There is no easy way to specify which methods of the object should have the behaviors. It is either all methods or no methods to get the behaviors. With Dynamic Decorator, however, individual methods can be specified to have the extra behaviors. For instance, in the last part of the code, only the method DetailsByLevel is specified when calling ObjectProxyFactory.CreateProxy of the Dynamic Decorator. As a result, only DetailsByLevel method gets the extra behaviors. The other methods of the object do not get the extra behaviors. Therefore, the Dynamic Decorator is more flexible and granular.

Last, behaviors defined in the Dynamic Decorator are able to access the target object and call context. It is extremely handy to get runtime information. For instance, the lambda expression of the last ObjectProxyFactory.CreateProxy in the above code accesses the target object to get the type, and accesses the call context to get the method name and argument value. On the other hand, I am not aware of any examples dealing with context or target information for Unity or Castle DynamicProxy.

There are other features of Dynamic Decorator that can be used to enhance behaviors. Please see the article Add Aspects to Object Using Dynamic Decorator for more details and examples.

Conclusion

The Dynamic Decorator is specifically designed to extend functionality of object. It is simple to use, flexible and can be used to write extremely powerful behaviors. It is a truly lightweight and feature-rich tool to extend object functionality and to add aspects to object. On the other hand, although the Unity and Castle DynamicProxy can be used to extend functionality of object, they have significant performance overhead and limited features to enhance the behaviors.

History

  • 26th July, 2011: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
Object-oriented (OO) is about "classes" not "objects". But I truly believe that "objects" deserve more our attentions. If you agree, read more on... Dynamic Object Programming (DOP), Component-Based Object Extender (CBO Extender), AOP Container and Dynamic Decorator Pattern.

Mobile development is not just another type of front end. The real challenge is actually in the back end: How to present meaningful information in time to mobile users with exponentially increased data flooding around? Here is my first mobile solution: SmartBars - Barcode Reader, Price Comparison and Coupons.

Gary lives in southeast Michigan. My first programming language is FORTRAN. For the last a few years, I have primarily focused on .NET technologies with Mobile Development as my newest interest.

Comments and Discussions

 
GeneralMy vote of 2 Pin
Member 998176212-Apr-13 4:47
Member 998176212-Apr-13 4:47 
GeneralRe: My vote of 2 Pin
Gary H Guo12-Apr-13 5:23
Gary H Guo12-Apr-13 5:23 
SuggestionNice artice, but... Pin
Philip Liebscher26-Jul-11 8:24
Philip Liebscher26-Jul-11 8:24 
GeneralRe: Nice artice, but... Pin
Gary H Guo26-Jul-11 9:42
Gary H Guo26-Jul-11 9:42 
AnswerRe: Nice artice, but... Pin
Philip Liebscher26-Jul-11 10:49
Philip Liebscher26-Jul-11 10:49 
GeneralRe: Nice artice, but... Pin
Gary H Guo26-Jul-11 17:48
Gary H Guo26-Jul-11 17:48 

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.