Click here to Skip to main content
Click here to Skip to main content

Emit Proxy

, 29 Dec 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
A dynamically generated proxy using .NET emit

Introduction

There are times we want to alter how a method is invoked without adding additional code into the method itself, either because the methods are a part of third party libraries or the additional code is out of place within the methods execution. It may be that we want to log the method calls, check for permissions or even cache results, these behaviors are not specific to individual methods and we would want the same behavior over multiple methods. Proxies provide a layer between calling a method and the method being called, this layer gives you an opportunity to intercept the method call and alter its behavior to suit your needs.

EmitProxy is a dynamic proxy allowing you to intercept all method invocations to any object that implements an interface. Once the method invocations are intercepted, you may perform actions before or after the invocation, alter the method parameters, alter return results or even bypass the execution altogether. The EmitProxy can be used to provide logging, caching, authentication, load balancing or any other over arching aspect that is applicable to multiple methods and does not belong in each individual method.

EmitProxy is focused on reducing the overhead normally associated with dynamic proxying. To achieve the high performance we require the proxy is generated at runtime using .NET Emit. The Emit framework gives the ability to write intermediate language (compiled .NET code) during runtime allowing the creation of dynamic code that performs as fast as pre compiled code. This offers significant advantages of dynamic proxies that use much slower reflection methods to generate proxies at runtime.

Background

The creation of proxies is a common and well defined design pattern, and is commonly done by writing intermediate wrapper objects to perform the additional operations required. However it is the creation of dynamic proxies at runtime that poses an interesting challenge, especially for strongly typed languages such as C#.

The .NET frame does supply the RealProxy class as part of the remoting services to help the creation of dynamic transparent proxies. The original use of the RealProxy was to create remote proxies, although they can be adapted to make general proxies on local objects, however these proxies are difficult to use and suffer from significant performance penalties.

John Mikhail developed the dynamic emit proxy, an alternative to using the real proxy to create dynamic proxies. Mikhail's dynamic emit proxy, similar to the EmitProxy discussed in this article, uses the .NET Emit framework to create a proxy during runtime for improved performance. Unfortunately Mikhail's proxy still uses reflection to invoke the proxied object hindering the performance of the proxy. The EmitProxy presented here does not use any reflection during invocations and as such performs significantly faster than Mikhail's emitted proxy.

Using the Code

To use the EmitProxy, you need an object that implements an interface you intend to proxy.

For this example, we have a simple interface with a method Echo and an object that implements it by just returning in the input parameter:

public interface ITestService
{
    string Echo(string echo);
}

public class TestService : ITestService
{
    public string Echo(string echo)
    {
        return echo;
    }
}

To create an EmitProxy, we call the Proxy extension method on TestService. As parameters, we define the interface to proxy on and an EmitProxyInterceptor delegate method. As a return result we are given a proxied object that implements the interface.

The Proxy method is an extension method on object, allowing it to be invoked on all types. However the object that calls the Proxy method has to implement the interface that is passed in (this comes from an interesting use of generics in extension methods). Additionally the class we pass needs to be an interface, if not a runtime error will be generated.

public class EmitProxyExample
{
    static void Main(string[] args)
    {
        ITestService service = new TestService();
        service = service.Proxy<ITestService>
        	(new EmitProxyInterceptor<ITestService>(MyInterceptorMethod));
        Console.Out.WriteLine(service.Echo("Hello")); //Prints "Hello World!";
    }

    public static object MyInterceptorMethod(ITestService proxiedObject,
    	string methodName, object[] parameters, EmitProxyExecute<ITestService> execute)
    {
        return execute(objectToInvoke, parameters[0]+" World!");
    }
}    

Every method call on our proxy will now go through the EmitProxyInterceptor first, giving it the opportunity to view and modify parameters, perform additional behavior or even forego the execution of the method itself.

The parameters passed into the interceptor method are:

  • objectToInvoke - The object we have proxied
  • methodName - The name of the method that was invoked
  • parameters - The parameters that were used when invoking the method
  • execute - A delegate used to perform the actual executing on the proxied object

The execute delegate passed is a method that will invoke the method we just proxied. It takes an object implementing the interface, and an object array of parameters, and returns the result of executing the method we just proxied.

It may seem an odd decision to require an object into the execute method, especially since most of the time it will just be the proxied object anyway. This was an intentional design decision allowing you to use the proxy as a load balancer. Each invocation could be executed on a different remote proxy spreading the execution load amongst a set of machines.

Selective Proxing

Having to handle every method invocation can be a bit of an overkill for most situations, it may be that we just want to proxy a handful of specific methods. The proxy method comes with an overload for just this situation. You can optionally pass a predicate delegate which can filter out any unwanted methods. The example below shows an instance where only methods starting with "Debug" will be proxied.

service.Proxy<ITestService>((methodInfo) => {
	return methodInfo.Name.StartsWith("Debug");
}, new EmitProxyInterceptor<ITestService>(MyInterceptorMethod));
In most cases, you would filter on a specific attribute. To make this easier, an overload exists to do just that. By passing in the attribute as a generic parameter, any method with this attribute in either the interface or proxied object will call the interceptor:

service.Proxy<ITestService, MyAttribute>
     (new EmitProxyInterceptor<ITestService>(MyInterceptorMethod));

Attribute Copying

Attributes are a common way to indicate to other frameworks how the framework should treat a particular class or method. Issues can be encountered when passing a proxy to one of these frameworks as typically the proxy object would not have the attributes expected by the framework. An example of this is working with WCF. When creating a singleton service, the class must have a [ServiceBehaviour(InstanceContextMode=InstanceContextMode.Single] attribute on the class to indicate its single host behavior. If we wanted to host a proxy rather than our actual service, then our generated proxy would need the same ServiceBehaviour attribute on it.

To avoid these issues, the emit proxy copies attributes from the proxied object and attaches them to the proxies class and equivalent methods. This would make an emit proxy appear to an external framework that it contains the same attributes as the object it is proxying.

Under the Hood

Most of the internals are written using .NET emit to create intermediate language. The emitted intermediate language is beyond the scope of this article, but a look at the C# equivalent should give a sufficient understanding of the internal workings on the emit proxy.

When the proxy method is called, the code goes off and creates a new class at runtime that implements the proxy interface. For each method on the interface, it will create two methods in the class. One method is an implementation of the interface, this will just call the EmitProxyInterceptor delegate. The other method is a static execute method, which is given to the EmitProxyInterceptor and will simply call the appropriate underlying method making the casts where needed.

Proxying the TestService example used above would emit a class equivalent to the follow C# class:

public class ManualEmitProxy : ITestService
{
    public EmitProxyInterceptor<ITestService> Interceptor;
    ITestService ProxiedObject;

    public string Echo(string echo)
    {
        return (string)Interceptor
		(ProxiedObject, "Echo", new object[] { echo }, ExecuteEcho);
    }

    public static object ExecuteEcho(ITestService service, params object[] parameters)
    {
        return service.Echo((string)parameters[0]);
    }
}

Once this class has been created, it is cached. Any further requests for this kind of proxy will use a new instance of the same class. This may make the creation of the first proxy computationally expensive but subsequent proxy creations are much quicker

Limitations

For the EmitProxy to work, it needs an object with an interface. The interface provides it with the list of methods that should be proxied and provides a type of variable the proxy can be assigned too. It is conceivable that a proxy could be developed to intercept virtual method invocations and dynamically extend a given class. However a virtual method proxy would add a great deal of complexity to an otherwise powerfully simplistic proxy so was left out of scope for this project.

Unfortunately the EmitProxy is not fully compatible with pass by reference and out parameters. The emit proxy will still function with these values, but the interceptor is unable to view or set these values correctly.

Performance

The speed of the emit proxy was identified as an important aspect in the development of the EmitProxy. Tests were done using a simple pass through proxy, that is a proxy that did nothing but pass the operation through to the proxied object. This was used as to not mix the speed of the proxying up with the speed of executing the proxy's logic.

Two tests were performed.

  • An echo method shown in the examples earlier in this article, this is an example of passing an object as a parameter (a string in this case) and returning an object
  • The second test is an add method, which will take two integers and return their sum. This was used as it would require the proxy to box both parameters and return result. Boxing should slow down the dynamic proxy showing its performance in a non ideal situation.

The results show the average invocation time over 10,000,000 invocations:

----Echo test----
No Proxy:                         9ns
EmitProxy:                      119ns
Manual Proxy:                    19ns
Manual Emit Proxy:              122ns
Transparent Proxy:            31441ns
Mikhail's Dynamic Emit Proxy:  8384ns

----Add Test (contains boxing)----
No Proxy:                         8ns
EmitProxy:                      180ns
Manual Proxy:                    17ns
Manual Emit Proxy:              183ns
Transparent Proxy:            36498ns
Mikhail's Dynamic Emit Proxy: 10091ns
  • No Proxy - No proxy was used, can be considered the time for one method invocation
  • Emit Proxy - Dynamic proxy that this article was about
  • Manual Proxy - Manually creating a proxy that implements the interface to call the proxied object
  • Manual Emit Proxy - Manually writing C# equivalent to what the emit proxy would generate (really a test to confirm the emitted intermediate language is as good as what the compiler generates)
  • Transparent Proxy - Implementation of a dynamic proxy using the .NET remoting RealProxy
  • Mikhail's Dynamic Emit Proxy - Emit proxy written and published on CodeProject by John Mikhail

Of the dynamic proxies (the proxies generated at runtime, the EmitProxy, Transparent Proxy and Mikhail's Dynamic Emit Proxy), the EmitProxy performed more than 50 times faster than the other two, showing it to be a clear improvement in terms of performance.

The boxing operations did slow down the add test for the dynamic proxies as expected, but the performance decrease is not significant enough to reduce its usefulness when handling with value types.

It is important to keep in mind that these are all very fast speeds and in most applications, the difference between 8ns, 120ns, and 31,000ns is not noticeable unless these proxies are in very heavy usage. EmitProxy was also developed to be small (only one CS file) and easy to use as well as fast, allowing EmitProxy to remain a useful piece of code even when not executing in high performance environments.

History

  • 4 November 2009 - First published
  • 26 December 2010 - Fixed Issue with multiple inheritance on interfaces. The proxy will now copy attributes from the proxied object. Allowed a predicate delegate to filter proxied methods.

License

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

Share

About the Author

Greenhippo

New Zealand New Zealand
No Biography provided

Comments and Discussions

 
Questionsupport generic method? Pinmemberonlyugly22-May-13 18:29 
GeneralMy vote of 5 PinmemberFeeble4-Feb-13 19:57 
GeneralRe: My vote of 5 PinmemberFeeble5-Feb-13 19:01 
GeneralExcellent Pinmemberjingiqin3-Dec-12 21:15 
GeneralSouth Beach Java Pinmemberanyamicha30-Dec-10 1:06 
GeneralRe: South Beach Java PinmemberDewey30-Dec-10 19:49 
GeneralMy vote of 5 PinmemberRuxo Zheng5-Aug-10 5:53 
GeneralNice Pinmemberdojohansen9-Nov-09 0:10 
Hi,
 
Nice article and great results. It's always fun to see how we can make things fast.
 
I'm giving a four star rating for this article. To get the full five from me would require more details on the implementation. As you mention yourself, the idea of proxying is not new and several other implementations exist, so what you are bringing to table here is really all about the implementation details, so I think it'd be nice if you had a bit more to say about them.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.1411023.1 | Last Updated 29 Dec 2010
Article Copyright 2009 by Greenhippo
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid