Click here to Skip to main content
15,879,535 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 241.8K   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

 
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 
AnswerSolution: Others methods parameters don't work Pin
Edgar Medrano5-Jan-11 12:18
Edgar Medrano5-Jan-11 12:18 
QuestionProxying just an interface also possible? Pin
jantje197823-Jan-04 0:49
jantje197823-Jan-04 0:49 
Great article, I'm wondering if it's also possibly to proxy an interface without using a base implementation? I tried to modify your example, but get a cast error. E.g.:

interface ISomething
{
void DoIt();
}

public class SomethingProxy : IProxyInvocationHandler
{
public static object NewInstance()
{
return ProxyFactory.GetInstance().Create(new SomethingProxy(), typeof(ISomething));
}

public object Invoke(object proxy, System.Reflection.MethodInfo method, object[] parameters)
{
System.Console.WriteLine("Proxy for method " + method.Name);
return null;
}
}

public void Test()
{
ISomething = (ISomething) SomethingProxy.NewInstance(); // Cast error
}
AnswerRe: Proxying just an interface also possible? Pin
jantje197823-Jan-04 1:08
jantje197823-Jan-04 1:08 
GeneralInterfaces Pin
Deyan Petrov14-Jan-04 21:26
Deyan Petrov14-Jan-04 21:26 
GeneralGreat! Pin
Deyan Petrov14-Jan-04 2:52
Deyan Petrov14-Jan-04 2:52 
QuestionWhat about RealProxy Pin
Whatabohr25-Nov-03 7:17
Whatabohr25-Nov-03 7:17 
GeneralExcellent! Pin
Eric Anderton25-Nov-03 5:45
Eric Anderton25-Nov-03 5:45 
GeneralRe: Excellent! Pin
John Mikhail25-Nov-03 13:11
John Mikhail25-Nov-03 13:11 

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.