Click here to Skip to main content
6,305,776 members and growing! (18,535 online)
Email Password   helpLost your password?
Languages » C# » General     Intermediate

Castle's DynamicProxy for .NET

By Hamilton Verissimo

Explains how to use DynamicProxy to intercept on object instances.
C#, Windows, .NET 1.1, .NET 2.0VS.NET2003, VS2005, Dev
Posted:14 Dec 2004
Updated:14 Dec 2004
Views:66,474
Bookmarked:41 times
Announcements
Loading...
 
Search    
Advanced Search
printPrint   Broken Article?Report       add Share
  Discuss Discuss   Recommend Article Email
13 votes for this article.
Popularity: 4.77 Rating: 4.29 out of 5
1 vote, 7.7%
1

2

3
5 votes, 38.5%
4
7 votes, 53.8%
5

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

About the Author

Hamilton Verissimo


Member
Hamilton Verissimo has been working with software development for 9 years. He's involved with a lot of open source projects to fill his spare time.
Occupation: Web Developer
Location: Brazil Brazil

Other popular C# articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 16 of 16 (Total in Forum: 16) (Refresh)FirstPrevNext
GeneralAdding Base Class PinmemberPablis5:37 23 Jul '06  
GeneralRe: Adding Base Class PinmemberHamilton Verissimo3:36 19 Aug '06  
GeneralHow to test if an object is a proxy PinmemberDavid marzo1:33 29 Aug '05  
AnswerRe: How to test if an object is a proxy PinmemberHamilton Verissimo7:16 29 Aug '05  
GeneralLight weight proxy Pinmembercklein6:58 12 Apr '05  
GeneralNice idea PinmemberRüdiger Klaehn8:18 24 Feb '05  
GeneralRe: Nice idea PinmemberHamilton Verissimo8:24 24 Feb '05  
GeneralRe: Nice idea PinmemberRüdiger Klaehn9:18 24 Feb '05  
GeneralRe: Nice idea PinmemberPortatofe6:20 2 Oct '08  
Generalsuggest adding a link to a FAQ, better yet simple code examples PinmemberBillWoodruff1:49 21 Dec '04  
GeneralRe: suggest adding a link to a FAQ, better yet simple code examples PinmemberHamilton Verissimo7:26 27 Dec '04  
GeneralRe: Got example of what this is good for? Pinmemberbeardman23:21 14 Dec '04  
GeneralRe: Got example of what this is good for? PinmemberRipplingCreek12:02 1 Dec '06  
GeneralGot example of what this is good for? Pinmemberronnyek6:17 14 Dec '04  
GeneralRe: Got example of what this is good for? PinmemberHamilton Verissimo7:49 14 Dec '04  
GeneralRe: Got example of what this is good for? Pinmemberronnyek7:52 14 Dec '04  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 14 Dec 2004
Editor: Smitha Vijayan
Copyright 2004 by Hamilton Verissimo
Everything else Copyright © CodeProject, 1999-2009
Web18 | Advertise on the Code Project