Click here to Skip to main content
15,891,253 members
Articles / Programming Languages / C#
Article

Dynamic Proxy Creation Using C# Emit

Rate me:
Please Sign up or sign in to vote.
4.81/5 (33 votes)
24 Nov 20036 min read 242.2K   3.8K   81   33
Creating a Java like Dynamic Proxy using the C# Emit feature

Introduction

The C# reflection package provides a powerful introspection mechanism that allows class information to be obtained dynamically at run time. However it has a shortcoming in the form of not having dynamic proxy support.

There are instances when functionality needs to be interjected before and/or after a method invocation. However, modifying the code to add those extra calls might not be feasible; whether it’s because the code in question is a third party library, whose source is not available, or if the code needs to be invoked for all methods in a given class. One example would be adding timing logic to each method call so you can monitor the execution time of a method. Modifying all the methods to add that logic before and after the method is time consuming and will clutter your code with redundant code. This would be an instance where the use of a proxy would greatly speed up the process, therefore decoupling the timing code from the business logic. A proxy class would intercept all incoming method invocations, allowing new code to be interjected before the method invocation and after.

This article will briefly outline how to use the C# Emit feature to dynamically create proxy classes. It will outline the use of the dynamic proxy with an example that will illustrate a security filter. This filter will inspect incoming method invocations and determine if the method is accessible to a given role. If accessible, the method will be invoked. Otherwise an error is thrown. This is performed in a dynamic proxy to relieve the burden of having to implement the security check in every method. This allows the check to be localized in one location for better code reuse.

The provided source code includes all the code examples provided here as well as the complete dynamic proxy generation source.

Building a Dynamic Proxy

Creating a dynamic proxy involves creating a class that traps incoming method invocations. There is no built in C# mechanism to do this, so this must be done dynamically at runtime. In order to accomplish this, we first define an interface IProxyInvocationHandler. With this interface, it is up to the user to define a proxy handler class that implements this interface. It does not matter what the definition of the class looks like, as long as the contract provided by the interface is fulfilled.

C#
public interface IProxyInvocationHandler { 
    Object Invoke( Object proxy, MethodInfo method, Object[] parameters ); 
}

Listing 1 – IProxyInvocationHandler Interface

The definition of the IProxyInvocationHandler is completely up to the user. Listing 2 shows an example of a proxy handler’s Invoke method that performs a security check.

C#
Public Object Invoke(Object proxy, System.Reflection.MethodInfo method, 
    Object[] parameters)
{
    Object retVal = null;
    // if the user has permission to invoke the method, the method
    // is invoked, otherwise an exception is thrown indicating they
    // do not have permission

    if ( SecurityManager.IsMethodInRole( userRole, method.Name ) ) {
        // The actual method is invoked
        retVal = method.Invoke( obj, parameters );
    } else {
        throw new IllegalSecurityException( "Invalid permission to invoke "
            + method.Name );
    }
    return retVal;
}

Listing 2 – Invoke Declaration

Dynamically Creating a Proxy

The dynamic proxy that will be generated works by implementing all the interfaces of a given type. The dynamic proxy will also maintain a reference to the invocation handler that the user defined. For every method declared in the type’s interface(s), a simple implementation is generated that makes a call to the proxy handler’s Invoke method. The method implementation has the same method signature as that defined in the interface.

A MethodInfo associated with the type, and the method’s parameters are passed in to the invocation handler. Here is where it gets a little tricky. The MethodInfo instance we pass to the Invoke method has to be the MethodInfo instance of the class that is going to be proxied. In order to accomplish that, we need access to that MethodInfo object without having access to the class we are going to proxy. Remember, we only pass in an instance of the invocation handler to this dynamically generated class, not the actual class instance that we are going to proxy. We get around this by creating a utility class that we can use to get the MethodInfo of a type by providing a unique name of the type and an index to indicate which MethodInfo we are interested in. A call to Type.GetMethods() returns a MethodInfo array irregardless of the number of times its called, it becomes safe to assume that by hard coding the index of the method we want to invoke inside the dynamic proxy, we will always get the same MethodInfo when the method is invoked. Listing 3 illustrates an example of what a method body in the dynamically created proxy class would look like.

C#
public void TestMethod( object a, object b ) { 
    if ( handler == null ) { 
        return; 
    } 
    // Param 1: Instance of this dynamic proxy class 
    // Param 2: TypeName is a unique key used to identify 
    //          the Type that is cached 
    // Param 3: 0 is the index of the method to retrieve from 
    //          the method factory. 
    // Param 4: Method parameters 
    handler.invoke( this, MethodFactory.GetMethod( TypeName, 0 ), 
        new object[] { a, b } ); 
} 

Listing 3 – Generated Dynamic Proxy

The call to MethodFactory.GetMethod takes the name of the object in order to lookup the Type of that object. Also passed in is the index of the MethodInfo object we want. When we generated this class dynamically, we iterated through the list of methods that this object declares; therefore we knew what index to the method is in the array.

All of this is accomplished using the C# Emit feature. This powerful feature allows Types to be created at runtime by writing out IL (intermittent language) code.

Diving into the intricate details of Emit is beyond the scope of this article and will not be covered in any great detail. What takes place is a new assembly and module is created. With an assembly and module created, a TypeBuilder can be constructed that represents a new Type. Various attributes can be defined such as the class scope, accessibility, etc. Once the type is created, fields, constructors, and methods can be constructed. For every method declared in the interface and any parent interfaces, a method is created similar to that outlined in Figure 3. The only differences between the methods are the number of arguments to be handled. Once the class and all methods have been defined, a new instance is created and returned to the caller. The caller can then cast the object to any of the interfaces passed in. The type that was just created is cached to improve on performance in case a new proxy instance of that type is needed.

Any subsequent call to a method of the generated class will then be calling the proxy method, which will in turn make a call to the Invoke method on the proxy handler. The user defined proxy handler can then perform any operation. Since the MethodInfo object is passed in to the proxy handler, the actual method can be invoked.

How to Proxy a Class

In order to create an object that is proxied, a class needs to be defined that has a corresponding interface with of all the methods of interest. This is needed because the interface is what defines the contract that is the basis for creating the dynamic proxy. Listing 4 illustrates what a class to be proxied would look like.

C#
public interface ITest { 
    void TestFunctionOne(); 
    Object TestFunctionTwo( Object a, Object b ); 
} 

public class TestImpl : ITest { 
    public void TestFunctionOne() { 
        Console.WriteLine( "In TestImpl.TestFunctionOne()" ); 
    } 
    public Object TestFunctionTwo( Object a, Object b ) { 
        Console.WriteLine( "In TestImpl.TestFunctionTwo( 
            Object a, Object b )" ); 
        return null; 
    } 
} 

public class TestBed { 
    static void Main( string[] args ) { 
        ITest test = (ITest)SecurityProxy.NewInstance( new TestImpl() );
        test.TestFunctionOne(); 
        test.TestFunctionTwo( new Object(), new Object() ); 
    } 
} 

Listing 4 – Creating a proxied object

The TestBed class shows how to create an instance of the proxied TestImpl class. The call to NewInstance takes an instance of TestImpl, which implements ITest. That instance is what will be proxied. The return value is a dynamic proxy object that itself implements ITest. In our example, invoking any method on that instance will cause the SecurityProxy.Invoke method to be called.

Current Limitations

Using a dynamic proxy currently has one limitation. In order to proxy an object, the object must have one or more interfaces that it implements. The reason being, we do not know what methods to proxy if an object with no interfaces is passed in. Sure we can go through all the methods that the instance has defined, but then methods like ToString, GetHashCode, etc, risk being proxied. Having a well defined contract using an interface allows the dynamic proxy to only proxy those methods outlined in the contract.

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


Written By
Web Developer
United States United States
John Mikhail is a Sr. Software Engineer working at eBuilt Inc. He has 7 years industry experience, with 4 years web application development experience, mostly in Java, but now using C# and .Net

Comments and Discussions

 
QuestionI can take support dynamic proxies to my ip Pin
Member 1260481526-Jun-16 13:09
Member 1260481526-Jun-16 13:09 
QuestionAlternative solution on aal.codeplex.com Pin
Ulrik Born21-May-14 20:50
Ulrik Born21-May-14 20:50 
QuestionApplicable license terms in case of use of this implementation in my own software Pin
sanikumbh3-Nov-11 5:47
sanikumbh3-Nov-11 5:47 
GeneralMy vote of 5 Pin
kingly4727025-Oct-11 21:39
kingly4727025-Oct-11 21:39 
GeneralMethods that have an out parameter Pin
Mike Ranzinger29-Nov-10 6:31
Mike Ranzinger29-Nov-10 6:31 
GeneralProblem with methods that contained more than 6 parameters Pin
punkiki6-Aug-08 4:26
punkiki6-Aug-08 4:26 
AnswerRe: Problem with methods that contained more than 6 parameters Pin
gilaz1110-Aug-08 6:50
gilaz1110-Aug-08 6:50 
GeneralProxy To Handle the InvokeRequired, Invoke(Delegate) boilerplate Pin
Member 473826724-Apr-08 5:31
Member 473826724-Apr-08 5:31 
GeneralLess complex solution PinPopular
karpov_sergey111112-May-07 22:28
karpov_sergey111112-May-07 22:28 
Questionhow Can be implemented class proxy? Pin
vgrigor13-Dec-06 23:29
vgrigor13-Dec-06 23:29 
Generalthanks! & some suggestions to solve identified problems Pin
25-Apr-05 0:46
suss25-Apr-05 0:46 
hi - great solution, thanks for the cool idea!

i've looked into the code and made some measurements. especially
comparing different solutions of the problem at hand. the results:

measurements:
-------------
proxy type: (no payload)             creation   execution   overhead
- plain implementation:                  .000      31.250       .000     .0%
- static direct wrapper:                 .000      31.250       .000     .0%
- static interface proxy (adorned):      .000     218.750    187.500  600.0%
- static interface proxy (transp.):      .000     203.125    171.875  550.0%
- static delegate proxy (adorned):       .000     796.875    765.625 2450.0%
- static delegate proxy (transp.):       .000     781.250    750.000 2400.0%
- dynamic code-gen proxy (adorned):    31.250     218.750    187.500  600.0%
- dynamic code-gen proxy (transp.):      .000     203.125    171.875  550.0%
- dynamyc remoting proxy (adorned):      .000    1093.750   1062.500 3400.0%
- dynamyc remoting proxy (transp.):      .000    1046.875   1015.625 3250.0%

proxy type: (heavy payload)          creation   execution   overhead
- plain implementation:                  .000    2187.500       .000     .0%
- static direct wrapper:                 .000    2203.125     15.625     .7%
- static interface proxy (adorned):      .000    2531.250    343.750   15.7%
- static interface proxy (transp.):      .000    2515.625    328.125   15.0%
- static delegate proxy (adorned):       .000    3109.375    921.875   42.1%
- static delegate proxy (transp.):       .000    3078.125    890.625   40.7%
- dynamic code-gen proxy (adorned):      .000    2500.000    312.500   14.3%
- dynamic code-gen proxy (transp.):      .000    2500.000    312.500   14.3%
- dynamyc remoting proxy (adorned):      .000    3500.000   1312.500   60.0%
- dynamyc remoting proxy (transp.):      .000    3484.375   1296.875   59.3%

notes on how to read the stats above:
1) - plain implementation: a simple implementation of the
     interfaces functionality
   - static direct wrapper: another coded implementation that simply delegates
     each call to another implementation
   - static interface proxy: a coded implementation that calls
     the handler interface in each method
   - static delegate proxy: a coded implementation that calls a
     c# <code>delegate</code> in each method
   - dynamic code-gen proxy: a dynamic implementation according
     to your solution
   - dynamic remoting proxy: a dynamic implementation using
     <code>RealProxy</code> which uses the .net remoting mechanism under the hood
2) adorned vs. transparent: 'adorned' adds some
   debugging information on each call whereas 'transparent' just performs pure
   delegation without any overhead of its own.
3) - result times: milliseconds
   - creation: the time measured for instantiating a proxy
     note: this normally just occurs once in the test program, so its
     meaningfulness is low
   - execution: the time measured for 10'000 iterations of invoking
     every method of the proxied interface once.
   - overhead: the time difference between an implementation compared
     to the plain worker implementation (in ms and percent).
4) no payload vs. heavy payload: without payload the
   plain implementation only incurs a very low cost whereas the heavy payload
   performs some expensive computation. this allows to measure the call overhead
   (no payload) in relation to the real life situation where the proxied object
   has 'something to do' (heavy payload).

my conclusions:
---------------
  I) c# delegates are surprisingly costly :-(
 II) the remoting mechanism is expensive as expected, but might be justified in
     situations where transparent behavior is desired over multi-tier boundaries
     and the like.
III) the code generation approach need not be any slower than a hand coded
     solution :-)

further suggestions:
--------------------
a) i have replaced the method lookup table in the <code>MetaDataFactory</code>
   with another approach. for each method of the interface, the generated type
   gets a static readonly field which gets initialized in a static constructor.
   that field is then directly used in the method's implementation instead of
   having to look it up first. this results in a class which can stand entirely
   on its own and additionally bears the benefit of being somewhat quicker too.
b) instead of having to specify whether the given type is an interface or a
   class by giving an according parameter, the <code>System.Type.IsInterface</code>
   property should be used.
c) the generated interface method implementations should be marked as explicit,
   thus avoiding problems when an interface combines multiple interfaces with
   conflicting methods (which would lead to ambiguities).
d) the generated code should perform either of a cast or an isinst instruction
   before returning the result of the handler to the caller of the proxy. this
   allows for detection or at least defensive behavior in case a handler behaves
   incorrectly.

problems identified:
--------------------
A) the provided implementation has a problem when an interface inherits from
   another interface which in turn has a base interface: with the recursive
   approach, the base interface gets implemented twice (as the
   <code>System.Type.getInterfaces()</code> method already returns an array with
   all base interfaces). this results in an 'invalid program' exception at runtime
   when the generated class get loaded by the type loader.
B) the provided implementation generates invalid byte code for methods that have
   parameters but no return type (void).

problem solutions:
------------------
A) in method <code>CreateType()</code> instead of:

   <code>
     if ( isObjInterface ) {
       type = CreateType( handler, new Type[] { objType }, typeName );
     } else {
       type = CreateType( handler, objType.GetInterfaces(), typeName );
     }
   </code>

   use something like:

   <code>
     Type[] interfacesToImplement = null;
     if ( objType.IsInterface ) {
       // gets _all_ parent interfaces!
       Type[] baseInterfaces = objType.GetInterfaces();
       if ( baseInterfaces != null ) {
         interfacesToImplement = new Type[ baseInterfaces.Length + 1 ];
         // iterate through the parent interfaces and add them to the
         // list of interfaces to implement
         for ( int i = 0; i < baseInterfaces.Length; i++ ) {
           interfacesToImplement[ i ] = baseInterfaces[ i ];
         }
         // add the given type
         interfacesToImplement[ baseInterfaces.Length ] = objType;
       } else {
         interfacesToImplement = new Type[] { objType };
       }
     } else {
       interfacesToImplement = objType.GetInterfaces();
     }
     if ( interfacesToImplement == null || interfacesToImplement.Length == 0 ) {
       throw new ArgumentException(
         objType.FullName + " has no interfaces to implement", "objType" );
     }
     type = CreateType( handler, interfacesToImplement, typeName );
   </code>

   and remove the recursive call in <code>GenerateMethod()</code> to itself.

B) instead of using <code>OpCodes.Stloc_0/1</code> and <code>OpCodes.Ldloc_0/1</code>
   hard coded in <code>GenerateMethod()</code> we should use a variable which gets
   initialized correctly in a way similar to:

   <code>
     bool hasReturnValue = !methodInfo.ReturnType.Equals( typeof( void ) );
     OpCode opCodeLoadParamArray = hasReturnValue ? OpCodes.Ldloc_1 : OpCodes.Ldloc_0;
     OpCode opCodeStorParamArray = hasReturnValue ? OpCodes.Stloc_1 : OpCodes.Stloc_0;
   </code>

code availability:
------------------
i can make the solution available which i have now if anybody desires it. it's not
yet on production level from a coding view but already incorporates some of the
suggested changes.
the solution contains the test program which i used to generate the stats at the
beginning. it also contains the various approaches used in the test program.

GeneralRe: thanks! & some suggestions to solve identified problems Pin
Guy Vinograd9-Jan-06 5:08
Guy Vinograd9-Jan-06 5:08 
GeneralProblem with many parameters and return type Pin
Juha Palomaki16-Nov-04 0:34
Juha Palomaki16-Nov-04 0:34 
GeneralIt's &quot;Reflection Emit&quot; Pin
Heath Stewart24-Jun-04 6:28
protectorHeath Stewart24-Jun-04 6:28 
GeneralRe: It's &quot;Reflection Emit&quot; Pin
Roger Alsing5-May-05 22:03
Roger Alsing5-May-05 22:03 
GeneralRe: It's &quot;Reflection Emit&quot; Pin
Heath Stewart6-May-05 6:50
protectorHeath Stewart6-May-05 6:50 
GeneralRe: It's &quot;Reflection Emit&quot; Pin
Roger Alsing8-May-05 20:19
Roger Alsing8-May-05 20:19 
GeneralRe: It's &quot;Reflection Emit&quot; Pin
Heath Stewart9-May-05 6:13
protectorHeath Stewart9-May-05 6:13 
QuestionProperties also possible? Pin
jantje197825-Jan-04 23:15
jantje197825-Jan-04 23:15 
GeneralOthers methods parameters don't work Pin
batiati24-Jan-04 10:22
batiati24-Jan-04 10:22 
GeneralRe: Others methods parameters don't work Pin
allaog27-Apr-06 0:09
allaog27-Apr-06 0:09 
JokeRe: Others methods parameters don't work Pin
nightjester22-Aug-06 7:49
nightjester22-Aug-06 7:49 
AnswerSolution: Others methods parameters don't work Pin
nightjester30-Aug-06 6:42
nightjester30-Aug-06 6:42 
GeneralRe: Solution: Others methods parameters don't work Pin
kayhustle24-Aug-08 14:26
kayhustle24-Aug-08 14:26 
GeneralRe: Solution: Others methods parameters don't work Pin
Lyubomir Dokov23-Nov-08 6:35
Lyubomir Dokov23-Nov-08 6:35 

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.