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

Castle's DynamicProxy for .NET

0.00/5 (No votes)
14 Dec 2004 1  
Explains how to use DynamicProxy to intercept on object instances.

Introduction

The proxy capabilities offered by the CLR are great. The ProxyAttribute, message sinks, and so on are really fantastic ideas leading to easy extension of the platform.

However, there are drawbacks. For example: to use proxy capabilities, your class must extend MarshalByRef or ContextBoundObject (which carries another semantic), and by doing so, you mess with your object model hierarchy. This can be very intrusive. Nevertheless, it's an option if you control the object model. But when things come from outside, there are no simple ways to achieve it.

This article explains how to use DynamicProxy, available at Castle Project, to create interceptors for your class in a fast and clean way, and yet have good performance as DynamicProxy does not use reflection to invoke the methods on the object instance.

How to use

DynamicProxy is able to proxy interfaces and concrete classes. You always have to supply the Type to be proxied and the interceptor instance. The interceptor will be invoked for each method invoked on the proxy, so you can carry on with your interceptor logic (transaction, logging, etc.) and then proceed or not with the invocation. If you choose to proceed, you must invoke the Proceed method on the invocation object supplied.

public class MyInterceptor : IInterceptor
{
    public object Intercept(IInvocation invocation, params object[] args)
    {
        DoSomeWorkBefore(invocation, args);

        object retValue = invocation.Proceed( args );

        DoSomeWorkAfter(invocation, retValue, args);

        return retValue;
    }
}

The IInvocation interface provides you with a lot of useful information:

public interface IInvocation
{
    object Proxy { get; }

    object InvocationTarget { get; set; }

    MethodInfo Method { get; }

    object Proceed( params object[] args );
}

For interfaces, you must also specify the object instance which will be the target of the invocations. Complex? Not really. Consider the following example:

public interface IMyInterface
{
    int Calc(int x, int y);
    int Calc(int x, int y, int z, Single k);
}

public class MyInterfaceImpl : IMyInterface
{
    public virtual int Calc(int x, int y)
    {
        return x + y;
    }

    public virtual int Calc(int x, int y, int z, Single k)
    {
        return x + y + z + (int)k;
    }
}

ProxyGenerator generator = new ProxyGenerator();

IMyInterface proxy = (IMyInterface) generator.CreateProxy( 
    typeof(IMyInterface), new StandardInterceptor(), new MyInterfaceImpl() );

This is a requirement as DynamicProxy needs a default target for the invocation - we'll explain why later.

Now, if you want to proxy concrete classes, there are also two requirements: the class can not be sealed and only virtual methods can be intercepted. The reason is that DynamicProxy will create a subclass of your class overriding all methods so it can dispatch the invocations to the interceptor. See the following example:

ProxyGenerator generator = new ProxyGenerator();

Hashtable proxy = (Hashtable) generator.CreateClassProxy( 
    typeof(Hashtable), new HashtableInterceptor() );

object value = proxy["key"]; // == "default"


public class HashtableInterceptor : StandardInterceptor
{
    public override object Intercept(IInvocation invocation, params object[] args)
    {
        if (invocation.Method.Name.Equals("get_Item"))
        {
            object item = base.Intercept(invocation, args);
            return (item == null) ? "default" : item;
        }
        return base.Intercept(invocation, args);
    }
}

If the class you want to proxy doesn't expose a default constructor, that's OK. You just need to supply the arguments to CreateClassProxy.

Mixins

Mixins are a kind of inheritance well-known in the C++ world. In a nutshell, the mixin-style of inheritance is the ability to mix a class with other (or others) which expose a single specific capability. DynamicProxy allows you to mix one class with others, resulting in a mixed proxy instance. If the mixins classes expose interfaces, they will be automatically exposed by the proxy instance.

public interface ISimpleMixin
{
    int DoSomething();
}

public class SimpleMixin : ISimpleMixin
{
    public int DoSomething()
    {
        return 1;
    }
}

ProxyGenerator generator = new ProxyGenerator();
GeneratorContext context = new GeneratorContext();

SimpleMixin mixin_instance = new SimpleMixin();

context.AddMixinInstance( mixin_instance );

SimpleClass proxy = (SimpleClass) generator.CreateCustomClassProxy( 
    typeof(SimpleClass), interceptor, context );

ISimpleMixin mixin = (ISimpleMixin) proxy;

Dynamic languages like Ruby are the heaven of mixins, as everything on the mixed module is exposed by the target class, but that's subject for another discussion.

Aspect# takes mixins a step further as it implements a protocol to allow the mixins instances to have a reference to the proxy instance. DynamicProxy however leaves this kind of decision up to the developer.

Under the hood

DynamicProxy generates a class on the fly similar to the following (pseudo)code:

public class ProxyGenerated : YourClass
{
  // Exposes all constructors

  
  // Overrides all methods

  
  public override int DoSomething(int x)
  {
    MethodInfo m = ldtoken currentMethod;
    IInvocation invocation = ObtainInvocationFor( deletegateForDoSomething, 
                             callableForDoSomething, m);
    return (int) _interceptor.Intercept( invocation, x );
  }

  private int BaseCallDoSomething(int x)
  {
    return base.DoSomething(x);
  }
}

As you can see, DynamicProxy relies on delegates to achieve good performance. The only performance bottleneck happens when boxing and unboxing value types. Basically, each delegate will have a pointer to another method that only invokes the base class method. This happens when you use the Proceed on the IInvocation instance.

Conclusion

For static weaving of assemblies, consider the nice and lean RAIL project. If your requirements are dynamic, consider DynamicProxy. I also need to thank all feedback and patches received through the Aspect# and Castle-devel mailing list. The users of DynamicProxy gave me the energy to refactor and tweak it time after time.

For updated information about DynamicProxy, check its site.

History

  • 13-Dec-2004 - Initial version.

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